如果我已经拥有该类的对象,我如何实例化某个类的新实例?

时间:2013-04-26 16:44:33

标签: clojure

我是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)

3 个答案:

答案 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)

您收到错误的原因与构造函数无关。

它不起作用的真正原因是因为newspecial form,而不是函数应用程序。与急切评估参数的函数应用程序不同,new不会。

它需要一个符号作为参数,并从中解析出Classname。

如您所见,(class an-object)不是符号,而是2个符号的列表。因此它失败了。

noahlz's answer解决此问题的原因是因为classname的符号是分别在let块中计算的。然后将new的正确表达式放在quote中,然后eval - 更换。所有这些都是lambda抽象的背后,所以它可以按需调用。

<强>外卖:

真正的问题是new是一种特殊形式,需要一个符号常量作为参数。

答案 2 :(得分:0)

我遇到了类似的问题,我想在处理Java互操作时将Class传递给函数。 "." dot special formnew灵活一些,您可以使用它来避免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"]
  )