我从Java调用Clojure并在传入的字符串上调用eval
。Java代码将保存对象,客户端代码可以指定在对象上运行的Clojure代码字符串。我知道如何从Java调用Clojure代码,但是如何在?
这就是我所拥有的。首先,一个简单的对象:
public class Helloer {
public String getGreeting() { return "Hello"; }
}
然后用一些样板代码来调用Clojure方法。
public static String call(Helloer helloer, String expression) throws Exception {
RT.loadResourceScript("EvalObject.clj");
final Var schrodEval = RT.var("eval-object", "eval-string");
final String result = (String) schrodEval.invoke(expression, helloer);
return result;
}
但后来我陷入了Clojure代码。对象传递得很好,但是如何将值传递给eval
?
这是我尝试过的:
(ns eval-object)
(defn eval-string [string this]
(eval (read-string string)))
(defn eval-string2 [string value]
(def this)
(binding [this value]
(eval (read-string string))))
(defn eval-string3 [string value]
(def this)
(eval (list 'binding (vector 'this 5) (read-string string))))
这些给出:
java.lang.Exception: Unable to resolve symbol: this in this context (NO_SOURCE_FILE:0)
然后我尝试构建一个定义this
的绑定子句:
(defn eval-string4 [string value]
(def this)
(eval (list 'do (list 'defonce 'this nil)
(list 'binding (vector 'this value) (read-string string)))))
但现在我明白了:
java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: Helloer@638bd7f1 (NO_SOURCE_FILE:0)
我错过了什么吗?是否可以将对象从Java传递到Clojure,然后传递给eval?
答案 0 :(得分:3)
将对象传递到eval中并不完全清楚你的意思。也许你可以更多地澄清目标?
然而,你可能正在寻找这样的东西:
(defn call-eval [helloer]
(eval `(.getGreeting ~helloer)))
(call-eval some-helloer)
=> "Hello"
有几点需要注意:
~
),以便直接使用对象而不仅仅是作为符号(.getGreeting helloer)
同样有效(而且效率更高,因为它不需要编译表单。通常,eval只是在运行时动态生成新代码或者想要读入并接受任意代码作为输入时需要(例如REPL本身)。答案 1 :(得分:2)
事实证明错误信息是正确的。我需要定义print-dup
。我为Helloer
类实现了一个简单的ID查找,并将print-dup
定义为:
(defmethod print-dup com.ziroby.Helloer [h stream]
(.write stream "#=(com.ziroby.Helloer/getById ")
(.write stream (str (.getId h)))
(.write stream ")"))
显然,Clojure需要一些方法来打印一个对象才能将它放入eval语句中。这个print-dup
创建一个Clojure语句,通过Helloer.getById
查找对象来“创建”该对象(它只是在hashmap中查找对象)。
正如@mikera所说,如果您接受来自调用者的任意代码或动态生成代码,您只需跳过这些箍。