2012-01-31 15 views

risposta

8

Ci sono due approcci di base:

  1. riflessione:

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

    lento, ma completamente dinamici.

  2. Estrarre gli argomenti precedentemente:

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

    ((Klass. ...) è il modo idiomatica crei (new Klass ...); esso viene convertito in quest'ultima forma al tempo di espansione macro.)

    Questo sarà più veloce se il il compilatore può dedurre quale costruttore sarà usato (probabilmente dovrai fornire suggerimenti di tipo appropriati - usa (set! *warn-on-reflection* true) per vedere se hai capito bene).

Il secondo approccio è, ovviamente, leggermente ingombrante. Se si prevede di creare molte istanze di Klass in molti punti del codice, è possibile scrivere una funzione di fabbrica appropriata. Se si prevede di trattare con molte classi in questo modo, si può astrarre il processo di funzioni factory che definiscono:

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

Infine, se il processo di definizione funzioni fabbrica stessa ha bisogno di essere completamente dinamico, si può fare qualcosa come il secondo approccio di Chouser a this question: definire una funzione piuttosto che una macro e averlo eval qualcosa come la sintassi (defn ...) sopra riportata (syntax-quoted = con backtick di fronte ad esso, non sono sicuro di come includere un backtick letterale in un post SO), tranne per il fatto che si desidera utilizzare fn anziché defn ed eventualmente saltare lo fname. La chiamata al compilatore sarà costosa, ma la funzione restituita funzionerà come qualsiasi funzione Clojure; vedere la risposta di Chouser di cui sopra per una discussione leggermente più lunga.

Per motivi di completezza, se si utilizza Clojure 1.3 o versioni successive e la classe Java coinvolta è in realtà un record Clojure, quindi una funzione di fabbrica posizionale sarà già stata creata con il nome di ->RecordName.

Problemi correlati