Ci sono due buoni modi per farlo. Quale è il migliore dipende dalla circostanza specifica.
Il primo è riflessione:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
Ecco come chiamare (new Integer "16")
... includere altri argomenti ctor necessari nel vettore a-array. Questo è facile, ma più lento in fase di esecuzione rispetto all'utilizzo di new
con suggerimenti di tipo sufficienti.
La seconda opzione è il più velocemente possibile, ma un po 'più complicato, e utilizza eval
:
(defn make-factory [classname & types]
(let [args (map #(with-meta (symbol (str "x" %2)) {:tag %1}) types (range))]
(eval `(fn [[email protected]] (new ~(symbol classname) [email protected])))))
(def int-factory (make-factory "Integer" 'String))
(int-factory "42")
Il punto chiave è il codice eval che definisce una funzione anonima, come make-factory
fa. Questo è lento - più lento rispetto all'esempio di riflessione sopra, quindi eseguirlo solo raramente, ad esempio una volta per classe. Ma avendo fatto ciò hai una normale funzione Clojure che puoi memorizzare da qualche parte, in una var come int-factory
in questo esempio, o in una mappa hash o in un vettore a seconda di come la userai. Indipendentemente da ciò, questa funzione di fabbrica funzionerà a piena velocità compilata, può essere sottolineata da HotSpot, ecc. E sarà sempre più veloce più veloce di rispetto all'esempio di riflessione.
Quando si trattano specificamente con le classi generate da deftype
o defrecord
, è possibile saltare la lista tipo dal momento che tali classi hanno sempre esattamente due ctors ognuna con diverse arietà. Questo permette qualcosa di simile:
(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 [[email protected]] (new ~(symbol recordname) [email protected])))))
(defrecord ExampleRecord [a b c])
(def example-record-factory (record-factory "ExampleRecord"))
(example-record-factory "F." "Scott" 'Fitzgerald)
Eccellente! La seconda opzione è ovviamente una tecnica molto generale. L'ho già usato in un altro modo. – chris