2012-01-31 39 views

回答

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)來查看是否正確)。

第二種方法當然稍微笨拙。如果您希望在代碼的許多位置構建大量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 [email protected])))) 

最後,如果定義工廠功能的過程本身需要是完全動態的,你可以這樣做像Chouser的第二種方法this question:定義了一個函數而不是宏,並且它具有類似於上面語法引用的(defn ...)的形式(syntax-quoted =在它前面帶有反引號;我不確定如何包含文字反引號在SO帖子中),除了你想要使用fn而不是defn並且可能跳過fname。對編譯器的調用將很昂貴,但返回的函數將像任何Clojure函數一樣執行;請參閱Chouser稍後討論的上述答案。

爲了完整起見,如果您使用的是Clojure 1.3或更高版本,並且所涉及的Java類實際上是Clojure記錄,則位置工廠函數將以名稱->RecordName創建。

相關問題