用clojure生成java bean

时间:2012-03-17 19:50:15

标签: java clojure interop

有没有办法在clojure中给出一个向量来轻松生成java bean?例如,给出这样的矢量:

[
    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 bean(我知道它们不一定是,但我希望它们是)。一种方法是在java中定义模型,然后使用clojure的普通java互操作来填充clojure代码中的模型,但我喜欢简化的clojure向量(或映射)扩展到(详细的)java bean的想法。

3 个答案:

答案 0 :(得分:3)

我不认为您的Java代码可以很好地与自动生成的Java bean兼容类一起使用。您需要至少在Java端有一个接口,以便了解Clojure将返回什么。没有它,你将不得不恢复:

Object result = callClojureLib(params);

然后,无论实际结果是否实现Java bean契约,您的Java代码都必须执行各种反射操作才能调用setter,因为您缺少类规范。

解决问题的另一种方法是使用java.util.Map接口作为Java和Clojure世界之间的契约。这样,您可以使用普通的Clojure映射作为传输对象,因为它们可以分配给java.util.Map

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

答案 1 :(得分:2)

远非完美,当您尝试使用它时可能会遇到许多无法预料的问题,但我认为您可以从以下内容开始:

  (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))
         ~@(def-access-methods (keys fields))
         ))

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

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

产地:

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

请注意,您需要编译geanbean名称空间。该实现使用atom来存储所有属性,因此请确保您理解权衡。

此外,在Clojure中工作时,您可能不想使用javabeans,但是您可以创建一些方法来获取和设置保持状态的原子。

答案 2 :(得分:0)

我想这应该是可能的,但我不确定你是否完全理解Clojure中的密钥(可能只是因为我误读了你的示例代码)。

:name之类的密钥属于clojure.lang.Keyword类型,而非StringInteger等等(您通常也不会在Clojure中声明类型)。它们通常用于地图(使用{}语法)来检索值,例如,以下代码从地图:key2中检索与{:key1 "hello", :key2 4}相关联的值。

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

我不确定您的示例是否试图说明:key1String 相关联,而:key2与{相关联{1}} 或者您认为Integer的类型为:key1。如果是前者,可能想要使用地图而不是矢量。

我担心我不会认为我对Java bean或您的用例有足够的了解,以便进一步提供帮助。