在Clojure中,如何动态使用java类?

vie*_*bel 10 clojure

在Clojure中,如何使用存储在变量中的java类?

我该如何修复以下代码?

(def a java.lang.String)
(new a "1"); CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: a
Run Code Online (Sandbox Code Playgroud)

为什么这个工作正常?

(def a str)
(a "1")
Run Code Online (Sandbox Code Playgroud)

vie*_*bel 8

最优雅的解决方案是编写construct相同的new但能够动态接收类:

 (defn construct [klass & args]
    (clojure.lang.Reflector/invokeConstructor klass (into-array Object args)))
 (def a HashSet)
 (construct HashSet '(1 2 3)); It works!!!
Run Code Online (Sandbox Code Playgroud)

这个解决方案克服了@mikera答案的限制(见评论).

特别感谢@MichałMarczyk让我意识到invokeConstructor回答我的另一个问题:Clojure:如何在函数内创建记录?.

另一种选择是将对构造函数的调用存储为匿名函数.在我们的情况下:

(def a #(String. %1))
(a "111"); "111"
Run Code Online (Sandbox Code Playgroud)


mik*_*era 7

以这种方式定义a时,会得到一个包含java.lang.Class的var

(def a java.lang.String)

(type a)
=> java.lang.Class
Run Code Online (Sandbox Code Playgroud)

然后你有两个选择:

答:通过使用反射API查找Java构造函数来动态构造新实例.请注意,正如Yehonathan指出的那样,您需要使用构造函数签名中定义的确切类(子类将无法工作,因为它找不到正确的签名):

(defn construct [klass & args]
  (.newInstance
    (.getConstructor klass (into-array java.lang.Class (map type args)))
    (object-array args)))

(construct a "Foobar!")
=> "Foobar!"
Run Code Online (Sandbox Code Playgroud)

B:使用Clojure的Java互操作构建,这需要一个eval:

(defn new-class [klass & args]
  (eval `(new ~klass ~@args)))

(new-class a "Hello!")
=> "Hello!"
Run Code Online (Sandbox Code Playgroud)

请注意,方法A相当快(在我的机器上快了大约60倍),我认为这主要是因为它避免了为每个eval语句调用Clojure编译器的开销.


sku*_*uro 6

问题是Clojure使用许多特殊形式实现Java互操作:

user=> (doc new)
-------------------------
new
Special Form
  Please see http://clojure.org/special_forms#new
nil
Run Code Online (Sandbox Code Playgroud)

这基本上意味着改变了"普通"Clojure语法,以便在调用Java时允许更加方便的构造.作为动态Java需求的天真反射解决方案,您可以利用eval:

user=> (def a String) ; java.lang package is implicitly imported
#'user/a
user=> `(new ~a "test") ; syntax quote to create the correct form
(new java.lang.String "test")
user=> (eval `(new ~a "test")) ; eval to execute
"test"
Run Code Online (Sandbox Code Playgroud)

相同的策略适用于所有其他互操作特殊形式,如方法调用.


编辑:还要看看@mikera答案,通过Java反射API获得更好的替代方案.