0

我是新手clojure,我的问题很简单,但让我绊倒了。
我想实例化一个类class,看起来像:

(new (class an-object))

只需让实例初始化即可。

例如,如果an-object是一个向量:

(new (class [1 2 3]))

我运行它,repl但只收到错误消息:

CompilerException java.lang.IllegalArgumentException:无法解析类名:(类 []),正在编译:(NO_SOURCE_PATH:1)

4

3 回答 3

2

幕后全是 Java,所以你必须遵守 Java 反射的限制。

PersistentVector 没有默认构造函数,所以(new (class [1 2 3]))永远不会工作。您需要检查可用的构造函数和静态方法并使用它们。

此外,这里有一个函数(不是宏),它适用于具有零参数构造函数的类,这就是你想要做的事情(嗯,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
于 2013-04-26T18:03:32.363 回答
2

我遇到了一个类似的问题,我想Class在处理 java 互操作时将 a 传递给函数。“ 。” 点特殊形式比它更灵活一点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"]
  )

于 2019-02-07T23:44:33.240 回答
0

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

它不起作用的真正原因是因为new是一种特殊形式,而不是功能应用程序。与急切地评估参数的函数应用程序不同,new它没有。

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

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

noahlz 的答案解决了这个问题的原因是因为类名的符号是在let块中单独计算的。然后将正确的表达式 withnew放在 aquote中,然后eval-uated。所有这些都在 lambda 抽象背后,因此可以按需调用。

带走:

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

于 2018-05-25T02:54:18.570 回答