2012-03-17 19 views
7

¿Hay alguna manera de generar fácilmente beans Java con un vector en clojure? Por ejemplo dado un vector de esta manera:generar beans Java con clojure

[ 
    String :key1 
    Integer :key2 
] 

me gustaría que para generar un código como éste:

public class NotSureWhatTheTypeWouldBeHere { 
    private String key1; 
    private Integer key2; 

    public NotSureWhatTheTypeWouldBeHere() {} 
    public NotSureWhatTheTypeWouldBeHere(String key1, Integer key2) { 
     this.key1 = key1; 
     this.key2 = key2; 
    } 

    public void setKey1(String key1) { 
     this.key1 = key1; 
    } 
    public String getKey1() { 
     return this.key1; 
    } 
    public void setKey2(Integer key2) { 
     this.key2 = key2; 
    } 
    public String getKey2() { 
     return this.key2; 
    } 

    // and equals,hashCode, toString, etc. 
} 

Para el contexto, me gustaría escribir una aplicación que está escrito en Java, pero llama a una biblioteca escrita en clojure. Eso significa que los valores de retorno deberían ser beans Java (sé que no tienen que serlo, pero me gustaría que lo fueran). Una forma sería definir el modelo en java y luego usar la interoperabilidad java normal de clojure para poblar el modelo en el código de clojure, pero me gusta la idea de un vector de clojure (o mapa) conciso que se expanda a un java (detallado) java.

Respuesta

3

No creo que su código Java pueda jugar muy bien con las clases autogeneradas de Java Bean. Usted necesita para tener al menos una interfaz en el lado de Java para tener sentido de lo que va a devolver Clojure. Sin eso, tendrá que volver a:

Object result = callClojureLib(params); 

Entonces, no importa si el resultado real implementa el contrato bean Java, el código Java tendrá que hacer todo tipo de magia reflexión para poder incluso llamar una setter, ya que le falta la especificación de clase.

Otro enfoque para el problema sería utilizar la interfaz java.util.Map como el contrato entre los mundos de Java y Clojure.De esta manera, es posible que utilices mapas de civil Clojure como objetos de transferencia, ya que son asignables a java.util.Map:

user=> (isa? (class {}) java.util.Map) 
true 
+0

+1 para usar java.util.Map como clase de interfaz. Además, sugiero usar cadenas como claves: funcionan tan bien como las palabras clave del lado de Clojure, pero son mucho más fáciles de asimilar para los usuarios de la API de Java ... – mikera

+0

esta es probablemente la respuesta correcta, pero pasa por alto el hecho de que específicamente no quiero un mapa en el lado de Java. Tengo una enorme franja de código Java que es service, dao y un montón de objetos modelo. Esperaba poder volver a escribir en Clojure de manera que los objetos del modelo pudieran generarse dinámicamente (reduciendo así el tamaño del código masivamente). Y desde la perspectiva del cliente, el código no es diferente al código original de Java. – Kevin

+0

Te sugiero que comiences a partir de una muestra del código Java cliente como te gustaría que fuera, y luego tratas de encontrar una interfaz común que pueda describir todos los casos que necesites y que puedas 'reificar' desde Clojure. El problema es realmente con tratar de generar beans Java sobre la marcha sin un contrato común con Java. – skuro

0

Supongo que debería ser posible, pero no estoy seguro de que entienda completamente las claves en Clojure (puede ser que esté malinterpretando su código de ejemplo).

Keys como :name son de tipo clojure.lang.Keyword, no String o Integer etc (también normalmente no declara tipos de Clojure). A menudo se usan en mapas (que usan la sintaxis {}) para recuperar valores, por ejemplo, el siguiente código recupera el valor asociado con :key2 del mapa {:key1 "hello", :key2 4}.

(get {:key1 "hello", :key2 4} :key2) 
4 

no estoy seguro de si su ejemplo está tratando de decir que tiene :key1 asociado con un valor String y :key2 asociado con un valorIntegero si cree :key1 es de tipo String. Si el primero, probablemente quiera usar un mapa en lugar de un vector.

Me temo que no creo que sepa lo suficiente sobre Java beans o su caso de uso en particular para ayudar mucho más.

2

lejos de ser perfecto y es probable que tenga una gran cantidad de problemas imprevistos al intentar usarlo, pero creo usted puede comenzar con algo como:

(ns genbean) 

    (defn -init [] 
    [[] (atom {})]) 

    (defn -toString 
    [this] 
    (str @(.state this))) 

    (defn -equals 
    [this other] 
    (= @(.state this) @(.state other))) 

    (defn -hashCode 
    [this] 
    (hash @(.state this))) 

    (defn set-field 
    [this key value] 
    (swap! (.state this) into {key value})) 

    (defn get-field 
    [this key] 
    (@(.state this) key)) 

    (defn gen-method-defs [fields] 
    (mapcat (fn [[name type]] [[(str "set" name) [type] 'void] 
          [(str "get" name) [] type]]) fields)) 

    (defn def-access-methods [fields] 
    (mapcat (fn [field] [`(defgetter ~field) `(defsetter ~field)]) fields)) 

    (defmacro defsetter [field] 
    `(defn ~(symbol (str "-set" field)) [this# value#] 
     (set-field this# ~(keyword field) value#))) 

    (defmacro defgetter [field] 
    `(defn ~(symbol (str "-get" field)) 
     [this#] 
     (get-field this# ~(keyword field)))) 

    (defmacro defbean [bean fields] 
    `(do 
     (gen-class 
      :main false 
      :state ~'state 
      :init ~'init 
      :name ~bean 
      :methods ~(gen-method-defs fields)) 
     [email protected](def-access-methods (keys fields)) 
     )) 

    (defbean com.test.Foo {Bar Integer Baz String What int}) 

lo podrá usar con el lado de java:

Foo f = new Foo(); 
    f.setBaz("hithere"); 
    f.setBar(12); 
    System.out.println("f = " + f); 
    Foo f2 = new Foo(); 
    System.out.println("f2.equals(f) = " + f2.equals(f)); 
    f2.setBaz("hithere"); 
    f2.setBar(12); 
    System.out.println("f2.equals(f) = " + f2.equals(f)); 
    System.out.println("(f2.hashCode() == f.hashCode()) = " + (f2.hashCode() == f.hashCode())); 

Produce:

f = {:Baz "hithere", :Bar 12} 
f2.equals(f) = false 
f2.equals(f) = true 
(f2.hashCode() == f.hashCode()) = true 

Tenga en cuenta que deberá compilar el espacio de nombres geanbean. La implementación usa un átomo para almacenar todas las propiedades, así que asegúrese de comprender las compensaciones.

Además, cuando trabaje en Clojure, probablemente no desee trabajar con javabeans, pero puede crear un par de métodos para obtener y establecer el átomo que mantiene el estado.

Cuestiones relacionadas