我是clojure
的新手,我的问题非常简单,但却让我感到沮丧
我希望通过class
实例化一个类get,如下所示:
(new (class an-object))
只需初始化实例即可。
例如,如果an-object
是向量:
(new (class [1 2 3]))
我在repl
中运行此操作,但只收到错误消息:
CompilerException java.lang.IllegalArgumentException:无法执行 解析classname :( class []),编译:(NO_SOURCE_PATH:1)
答案 0 :(得分:2)
在幕后,它都是Java,所以你必须遵守Java反射的限制。
PersistentVector
doesn't have a default constructor,因此(new (class [1 2 3]))
将无法正常工作。您需要检查可用的构造函数和静态方法并使用它们。
此外,这是一个函数(不是一个宏),适用于具有零arg构造函数的类,你正在尝试做的事情(好吧,defn
是一个宏,所以`和〜工作,但这是另一个故事):
(defn new-instance [obj & args]
(let [clazz (class obj)]
(eval `(new ~clazz ~@args))))
=> (new-instance "1224")
""
=> (new-instance (new java.util.HashMap))
{}
;; can even pass arguments to constructors
=> (new-instance 1234 "42")
42
答案 1 :(得分:0)
您收到错误的原因与构造函数无关。
它不起作用的真正原因是因为new
是special form,而不是函数应用程序。与急切评估参数的函数应用程序不同,new
不会。
它需要一个符号作为参数,并从中解析出Classname。
如您所见,(class an-object)
不是符号,而是2个符号的列表。因此它失败了。
noahlz's answer解决此问题的原因是因为classname的符号是分别在let
块中计算的。然后将new
的正确表达式放在quote
中,然后eval
- 更换。所有这些都是lambda抽象的背后,所以它可以按需调用。
<强>外卖:强>
真正的问题是new
是一种特殊形式,需要一个符号常量作为参数。
答案 2 :(得分:0)
我遇到了类似的问题,我想在处理Java互操作时将Class
传递给函数。 "." dot special form比new
灵活一些,您可以使用它来避免eval
或宏
(defn new-instance
([clazz]
(. clazz newInstance))
([clazz args]
(new-instance clazz (map class args) args))
([clazz ctor-classes args]
(->
(. clazz getConstructor (into-array Class ctor-classes))
(.newInstance (into-array Object args)))))
(comment
(new-instance String)
;;=> ""
(new-instance (class "abc"))
;;=> ""
(new-instance String ["foo"])
;;=> "foo"
(new-instance Long [String] ["11"])
;;=> 11
(new-instance Long [Long/TYPE] [42]) ; special case for primitive types
;;=> 11
(new-instance java.io.File [(clojure.java.io/file "/tmp") "test.txt"])
;;=> #object[java.io.File 0x5c69702f "/tmp/test.txt"]
(new-instance java.io.File ["/tmp" "test2.txt"])
;;=> #object[java.io.File 0x39859951 "/tmp/test2.txt"]
)