使用构造函数args列表调用Java new而不是args本身(在Clojure中)

时间:2012-01-31 17:32:25

标签: constructor clojure new-operator

我知道我可以在Clojure中实例化这样的Java类:

(new Classname args*)

假设我提交了构造函数使用的args的列表。那我怎么实例化这个类呢?我无法使用apply,因为new不是函数。

1 个答案:

答案 0 :(得分:8)

有两种基本方法:

  1. 反射:

    (clojure.lang.Reflector/invokeConstructor Klass (to-array [arg ...]))
    

    缓慢但完全动态。

  2. 事先解压缩参数:

    (let [[arg1 arg2 ...] args]
      (Klass. arg1 arg2 ...))
    

    (Klass. ...)是写(new Klass ...)的惯用方式;它在宏扩展时转换为后一种形式。)

    如果编译器可以推断出将使用哪个构造函数,那么这将更快(您可能需要提供适当的类型提示 - 使用(set! *warn-on-reflection* true)来查看您是否正确)。

  3. 第二种方法当然略显笨拙。如果您希望在代码中的许多位置构建大量Klass实例,则可以编写适当的工厂函数。如果您希望以这种方式处理许多类,则可以抽象出定义工厂函数的过程:

    (defmacro deffactory [fname klass arg-types]
      (let [params (map (fn [t]
                          (with-meta (gensym) {:tag t}))
                        arg-types)]
        `(defn ~(with-meta fname {:tag klass}) ~(vec params)
           (new ~klass ~@params))))
    

    最后,如果定义工厂函数本身的过程需要完全动态化,那么你可以做一些像Chouser第二种方法this question:定义一个函数而不是一个宏并让它eval就像上面的语法引用(defn ...)形式一样(语法引用=前面有反引号;我不知道如何在SO帖子中包含文字反引号),除非你想要使用{{ 1}}而不是fn,可能会跳过defn。对编译器的调用将是昂贵的,但返回的函数将执行任何Clojure函数;请参阅Chouser的上述答案进行稍长时间的讨论。

    为了完整起见,如果你使用的是Clojure 1.3或更高版本,并且所涉及的Java类实际上是一个Clojure记录,那么就已经在fname的名称下创建了一个位置工厂函数。 / p>