2012-01-31 10 views

Odpowiedz

8

Istnieją dwa podstawowe podejścia:

  1. refleksji:

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

    Powolny, ale całkowicie dynamiczne.

  2. rozpakować argumenty uprzednio:

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

    ((Klass. ...) jest sposobem na język pisania (new Klass ...), to jest przekształcany w drugiej formie w makro czasie rozprężania.)

    To będzie szybciej, jeśli kompilator może wydedukować, który konstruktor zostanie użyty (prawdopodobnie będziesz musiał podać odpowiednie wskazówki typu - użyj (set! *warn-on-reflection* true), aby sprawdzić, czy masz rację).

Drugie podejście jest oczywiście nieco nieporęczne. Jeśli spodziewasz się zbudować wiele instancji Klass w wielu miejscach kodu, możesz napisać odpowiednią funkcję fabryczną. Jeśli oczekujesz do czynienia z wielu klas w ten sposób można abstrakcyjny dala proces definiowania funkcji fabrycznych:

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

Wreszcie, jeśli proces definiowania funkcji fabrycznych, sam musi być całkowicie dynamiczny, można zrobić coś podobnie jak drugie podejście Chousera do this question: zdefiniuj funkcję, a nie makro, i nadaj jej eval coś podobnego do powyższej składni (defn ...) (składnia cytowana = z backtick przed nim; nie jestem pewien, jak dołączyć dosłowny backtick w poście SO), z wyjątkiem tego, że chcesz używać fn zamiast defn i ewentualnie pomiń fname. Wywołanie kompilatora będzie kosztowne, ale zwrócona funkcja będzie działać tak, jak każda funkcja Clojure; patrz wyżej wspomniana odpowiedź Chousera na nieco dłuższą dyskusję.

Dla większej kompletności, jeśli używasz Clojure 1.3 lub nowszego, a klasa Java, która jest zaangażowana, jest faktycznie rekordem Clojure, to fabryczna funkcja pozycyjna zostanie już utworzona pod nazwą ->RecordName.