2012-03-17 3 views
7

クロージャーでベクトルを指定してJava Beanを簡単に生成する方法はありますか?例えば、このようなベクターを与えられた:は、クロージャーでjava beansを生成します

[ 
    String :key1 
    Integer :key2 
] 

私はそれがこのようなコードを生成したいと思います:コンテキストについて

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. 
} 

を、私はJavaで書かれているアプリを書きたいのですが、 clojureで書かれた図書館への呼び出し。つまり、戻り値はJava Beansでなければなりません(私は彼らがいなくてもよいと思っていますが、そうしたいと思います)。 1つの方法は、javaでモデルを定義し、次にclojureの通常のJava interopを使用してモデルをクロージャーコードに入力することですが、(冗長な)Java Beanに展開する簡潔なクロージャーベクトル(またはマップ)の考え方が好きです。

答えて

3

あなたのJavaコードは自動生成されたJava Bean準拠のクラスではうまく動作しません。 には、Java側で少なくともClojureが返す予定を理解するためのインタフェースが必要です。実際の結果は、Java Beanの契約を実装している場合、Javaコードでも呼び出すことができるように反射魔法のすべてのソートを行う必要があります、

Object result = callClojureLib(params); 

その後、どんなに:それがなければ、あなたがに戻す必要がありますあなたがクラス仕様を見落としているので、setter。

もう1つのアプローチは、JavaとClojureの世界の間の契約としてjava.util.Mapインターフェイスを使用することです。

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

インターフェイスクラスとしてjava.util.Mapを使用する場合+1。私はさらにStringsをキーとして使うことをお勧めします:これらはClojure側のキーワードと同様に動作しますが、Java APIユーザーにとってははるかに簡単です。 – mikera

+0

これはおそらく正しい答えですが、具体的には、Java側でマップを必要としません。私はサービス、DAO、およびモデルオブジェクトの束であるJavaコードの膨大な幅を持っています。私はモデルオブジェクトを動的に生成できるようにclojureに書き直すことができました(コードサイズを大幅に縮小する)。クライアントの観点から見ると、コードは元のJavaコードと変わりません。 – Kevin

+0

クライアントJavaコードのサンプルから始めることをお勧めします。そして、必要なすべてのケースを記述し、Clojureから 'reify'できる共通のインタフェースを見つけることをお勧めします。この問題は、Java側と共通の契約を結ばずにJava Beanをオンザフライで生成しようとすることに本当にあります。 – skuro

0

私はそれが可能であるはずですが、Clojureのキーを完全に理解しているかどうかはわかりません(私はあなたのサンプルコードを誤読しているかもしれません)。

:nameなどのキーは、StringまたはIntegerなどではなく、タイプClojureではありませんこれらの値は、値を取得するために({}構文を使用する)マップでよく使用されます。たとえば、次のコードでは、の:key2に関連付けられた値が取得されます。

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

は、私はあなたの例では、Integerに関連付けられたString:key2に関連した:key1を持っているか、あなたが考える場合:key1はタイプStringであると言うしようとしているかはわかりません。前者の場合は、おそらくベクトルの代わりにマップを使用したいと考えています。

私は、Java Beansやユースケースについて十分に知っているとは思えません。

2

ファー完璧から、あなたはそれを使用しようとすると、それはおそらく、予期せぬ問題をたくさん持っているが、私は思う:彼らはjava.util.Mapに割り当てているので、この方法では、あなただけの、転送オブジェクトとしてプレーンなClojureのマップを使用することができます

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

が生成されます:Java側からそれを使用して

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

:あなたのような何かを始めることができます

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

geanbean名前空間をコンパイルする必要があることに注意してください。この実装では、すべてのプロパティを格納するためにアトムが使用されるため、トレードオフを理解してください。

また、Clojureで作業する場合、おそらくjavabeansを使用したくないかもしれませんが、状態を保持するアトムを取得および設定するための2つのメソッドを作成できます。

関連する問題