Clojure:从String类名创建新实例 [英] Clojure: creating new instance from String class name

查看:129
本文介绍了Clojure:从String类名创建新实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Clojure中,给定一个类名作为字符串,我需要创建一个新的类的实例。换句话说,我将如何在

中实现新的实例类名。

 (def my-class-name org.myorg.pkg.Foo)
;使用参数1,2和3调用org.myorg.pkg.Foo的构造函数
(new-instance-from-class-name my-class-name 1 2 3)



我正在寻找比

更优雅的解决方案


  • 在类

  • 上使用eval,load-string,...
  • 在构造函数上调用Java newInstance方法


在实践中,我将使用defrecord创建的类。因此,如果对于该场景有任何特殊的语法,我会很感兴趣。

解决方案



第一个是反射:

 
(clojure.lang.Reflector / invokeConstructor
(resolve(symbolInteger))
(to-array [16]))

这就像调用(new Integer16) ...包括to-array向量中需要的任何其他ctor参数。这很容易,但在运行时比使用具有足够类型提示的 new 更慢。



第二个选项是快,但有点复杂,并使用 eval

 
make-factory [classname&types]
(let [args(map#(with-meta(symbol(strx%2)){:tag%1})types(range))]
(eval`(fn [〜@ args](new〜(symbol classname)〜@ args))))

(def int-factory(make-factoryInteger

(int-factory42)

关键是eval代码定义一个匿名函数,因为 make-factory 会。这是 - 比上面的反射示例慢,所以只做尽可能少,如每个类一次。但是,在这个例子中,你有一个常规的Clojure函数可以存储在某个地方,如 int-factory ,或者根据哈希映射或向量你将如何使用它。无论如何,这个工厂函数将以完全编译的速度运行,可以由HotSpot等内联,并且总是比反射示例运行更快



当你专门处理由 deftype defrecord 生成的类时,可以跳过类型列表,因为这些类总是有两个ctors,每个都有不同的arities。这允许类似:

 
(defn record-factory [recordname]
(let [recordclass ^ ))
max-arg-count(apply(map#(count(.getParameterTypes%))
(.getConstructors recordclass)))
args %))(range( - max-arg-count 2)))]
(eval`(fn [〜@ args](new〜(symbol recordname)〜@ args)))))


(defrecord ExampleRecord [abc])

(example-record-factory(record-factoryExampleRecord))

-record-factoryFScott'Fitzgerald)


In Clojure, given a class name as a string, I need to create a new instance of the class. In other words, how would I implement new-instance-from-class-name in

(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) 

I am looking for a solution more elegant than

  • calling the Java newInstance method on a constructor from the class
  • using eval, load-string, ...

In practice, I will be using it on classes created using defrecord. So if there is any special syntax for that scenario, I would be quite interested.

解决方案

There are two good ways to do this. Which is best depends on the specific circumstance.

The first is reflection:

(clojure.lang.Reflector/invokeConstructor
  (resolve (symbol "Integer"))
  (to-array ["16"]))

That's like calling (new Integer "16") ...include any other ctor arguments you need in the to-array vector. This is easy, but slower at runtime than using new with sufficient type hints.

The second option is as fast as possible, but a bit more complicated, and uses 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")

The key point is to eval code that defines an anonymous function, as make-factory does. This is slow -- slower than the reflection example above, so only do it as infrequently as possible such as once per class. But having done that you have a regular Clojure function that you can store somewhere, in a var like int-factory in this example, or in a hash-map or vector depending on how you'll be using it. Regardless, this factory function will run at full compiled speed, can be inlined by HotSpot, etc. and will always run much faster than the reflection example.

When you're specifically dealing with classes generated by deftype or defrecord, you can skip the type list since those classes always have exactly two ctors each with different arities. This allows something like:

(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)

这篇关于Clojure:从String类名创建新实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆