2012-01-31 14 views

Respuesta

8

Hay dos enfoques básicos:

  1. Reflexión:

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

    lenta, pero completamente dinámicos.

  2. Descomprimir los argumentos a priori:

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

    ((Klass. ...) es la forma idiomática de escribir (new Klass ...), sino que se convierte en el último formulario en tiempo de expansión de la macro.)

    Esto será más rápido si el el compilador puede deducir qué constructor se usará (probablemente necesite proporcionar sugerencias de tipo apropiadas: use (set! *warn-on-reflection* true) para ver si lo tiene correcto).

El segundo enfoque es, por supuesto, un poco difícil de manejar. Si espera construir muchas instancias de Klass en muchos lugares de su código, puede escribir una función de fábrica adecuada. Si usted espera para hacer frente a muchas clases de esta manera, se puede abstraer el proceso de las funciones de la fábrica que se establezcan:

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

Por último, si el proceso de definirse las funciones de la fábrica tiene que ser completamente dinámico, puede hacer algo como el segundo acercamiento de Chouser al this question: defina una función en lugar de una macro y tenga eval algo así como el formulario (defn ...) sintaxis-citado arriba (sintaxis-citado = con backtick en frente de él; no estoy seguro de cómo incluir un backtick literal en una publicación SO), excepto que deseará usar fn en lugar de defn y posiblemente omita el fname. La llamada al compilador será costosa, pero la función devuelta funcionará como lo haría cualquier función de Clojure; vea la respuesta mencionada de Chouser para una discusión un poco más larga.

Para completar, si está utilizando Clojure 1.3 o posterior y la clase Java involucrada es en realidad un registro Clojure, entonces una función de fábrica de posición ya se habrá creado con el nombre ->RecordName.

Cuestiones relacionadas