代码之家  ›  专栏  ›  技术社区  ›  chris

Clojure:从字符串类名创建新实例

  •  16
  • chris  · 技术社区  · 14 年前

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

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

    我在寻找一个比

    • 在类的构造函数上调用Java newInstance方法
    • 使用eval,加载字符串。。。

    4 回复  |  直到 14 年前
        1
  •  25
  •   Chouser    14 年前

    有两个好方法可以做到这一点。哪种最好取决于具体情况。

    首先是反思:

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

    就像打电话一样 (new Integer "16") 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")
    

    关键是评估定义匿名函数的代码,如 make-factory 做。这是 int-factory 在本例中,或者在哈希映射或向量中,具体取决于您将如何使用它。不管怎样,这个工厂函数将以完全编译的速度运行,可以被HotSpot等内联,并且总是运行得很快 更快

    当您专门处理由 deftype defrecord ,您可以跳过类型列表,因为这些类总是正好有两个具有不同arity的actor。这允许如下操作:

    (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)
    
        2
  •  4
  •   Rayne    14 年前

    user=> (defmacro str-new [s & args] `(new ~(symbol s) ~@args))
    #'user/str-new
    user=> (str-new "String" "LOL")
    "LOL"
    

    查看Michal对这个宏的局限性的评论。

        4
  •  3
  •   miner49r    13 年前

    在Clojure1.3中,defrecord将使用前面带有“->”的记录名自动定义工厂函数。类似地,采用映射的变量将是以“map->”开头的记录名。

    user=> (defrecord MyRec [a b])
    user.MyRec
    user=> (->MyRec 1 "one")
    #user.MyRec{:a 1, :b "one"}
    user=> (map->MyRec {:a 2})
    #user.MyRec{:a 2, :b nil}
    

    这样的宏应该可以从记录类型的字符串名称创建实例:

    (defmacro newbie [recname & args] `(~(symbol (str "->" recname)) ~@args))