Hay dos buenas formas de hacerlo. Lo mejor depende de la circunstancia específica.
La primera es la reflexión:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
Eso es como decir que (new Integer "16")
... incluyen cualquier otro argumento ctor que necesita en el vector a matriz. Esto es fácil, pero más lento en tiempo de ejecución que usar new
con suficientes sugerencias de tipo.
La segunda opción es lo más rápido posible, pero un poco más complicado, y utiliza 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")
El punto clave es el código eval que define una función anónima, como make-factory
hace. Esto es lento - más lento que el ejemplo de reflexión anterior, por lo tanto, hágalo con la menor frecuencia posible, como una vez por clase. Pero una vez hecho esto, tiene una función Clojure normal que puede almacenar en algún lugar, en una var como int-factory
en este ejemplo, o en un hash-map o vector, dependiendo de cómo lo va a usar. A pesar de todo, esta función de fábrica se ejecutará a la velocidad compilada completa, puede ser ingresada por HotSpot, etc. y siempre se ejecutará mucho más que que el ejemplo de reflexión.
Cuando se trata específicamente de clases generadas por deftype
o defrecord
, puede omitir la lista de tipos ya que esas clases siempre tienen exactamente dos ctors cada una con aries diferentes. Esto permite que algo como:
(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)
¡Excelente! La segunda opción es obviamente una técnica muy general. Ya lo he usado de otra manera. – chris