chr*_*ris 17 constructor clojure
在Clojure中,给定一个类名作为字符串,我需要创建一个新的类实例.换句话说,我如何在中实现new-instance-from-class-name
(def my-class-name "org.myorg.pkg.Foo")
; calls constructor of org.myorg.pkg.Foo with arguments 1, 2 and 3
(new-instance-from-class-name my-class-name 1 2 3)
Run Code Online (Sandbox Code Playgroud)
我正在寻找一个更优雅的解决方案
在实践中,我将在使用defrecord创建的类上使用它.因此,如果该场景有任何特殊语法,我会非常感兴趣.
Cho*_*ser 24
有两种很好的方法可以做到这一点.哪个最好取决于具体情况.
首先是反思:
(clojure.lang.Reflector/invokeConstructor (resolve (symbol "Integer")) (to-array ["16"]))
这就像调用(new Integer "16") ...包括你在to-array向量中需要的任何其他ctor参数.这很容易,但在运行时比使用new足够类型的提示要慢.
第二种选择是尽可能快,但有点复杂,并使用eval:
(defn make-factory [classname & types]
(let [args (map #(with-meta (symbol (str "x" %2)) {:tag %1}) types (range))]
(eval `(fn [~@args] (new ~(symbol classname) ~@args)))))
(def int-factory (make-factory "Integer" 'String))
(int-factory "42")
关键是eval代码定义匿名函数,也是make-factory如此.这很慢 - 比上面的反射例子慢,所以只能尽可能不频繁地进行,例如每班一次.但是这样做了你有一个常规的Clojure函数,你可以存储在某个地方,在int-factory这个例子中的var 中,或者在哈希映射或向量中,具体取决于你将如何使用它.无论如何,这个工厂函数将以完全编译的速度运行,可以由HotSpot等内联,并且总是比反射示例运行得快得多.
当你专门处理由deftypeor 生成的类时defrecord,你可以跳过类型列表,因为这些类总是只有两个ctors,每个ctors有不同的arities.这允许类似于:
(defn record-factory [recordname]
(let [recordclass ^Class (resolve (symbol recordname))
max-arg-count (apply max (map #(count (.getParameterTypes %))
(.getConstructors recordclass)))
args (map #(symbol (str "x" %)) (range (- max-arg-count 2)))]
(eval `(fn [~@args] (new ~(symbol recordname) ~@args)))))
(defrecord ExampleRecord [a b c])
(def example-record-factory (record-factory "ExampleRecord"))
(example-record-factory "F." "Scott" 'Fitzgerald)