使用clojure生成java bean

使用clojure生成java bean,java,clojure,interop,Java,Clojure,Interop,在clojure中给定一个向量,有没有一种方法可以轻松地生成java bean?例如,给定如下向量: [ String :key1 Integer :key2 ] public class NotSureWhatTheTypeWouldBeHere { private String key1; private Integer key2; public NotSureWhatTheTypeWouldBeHere() {} public NotSu

在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.
}
我希望它生成如下代码:

[
    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编写的库。这意味着返回值应该是JavaBean(我知道它们不必是,但我希望它们是)。一种方法是用java定义模型,然后使用clojure的普通java互操作在clojure代码中填充模型,但我喜欢简洁的clojure向量(或映射)扩展为(详细的)java bean的想法

我想这应该是可能的,但我不确定您是否完全理解Clojure中的键(可能只是我误读了您的示例代码)

:name
这样的键属于
clojure.lang.Keyword
类型,而不是
String
Integer
等(您通常也不会在clojure中声明类型)。它们通常在映射(使用{}语法)中用于检索值,例如,以下代码从映射中检索与
:key2
相关的值
{:key1“hello”,:key2 4}

(get {:key1 "hello", :key2 4} :key2)
4
我不确定您的示例是否试图说您的
:key1
字符串
值关联,而
:key2
整数
值关联,或者您是否认为
:key1
字符串
类型。如果是前者,可能需要使用地图而不是向量


很抱歉,我认为我对Java bean或您的用例了解不够,无法提供更多帮助。

我认为您的Java代码无法很好地处理自动生成的Java bean兼容类。您需要在Java端至少有一个接口,以了解Clojure将返回什么。否则,您将不得不恢复到:

Object result = callClojureLib(params);
然后,无论实际结果是否实现了JavaBean契约,您的Java代码都必须执行各种反射向导才能调用setter,因为您缺少类规范

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

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

远不是完美的,当你尝试使用它时,它可能会有很多不可预见的问题,但我认为你可以从以下内容开始:

  (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,但可以创建两个方法来获取和设置持有状态的atom。

+1使用java.util.Map作为接口类。我进一步建议使用字符串作为键:在Clojure端,字符串和关键字一样有效,但Java API用户更容易找到它们……这可能是正确的答案,但它忽略了一点,即我特别不希望Java端有映射。我有大量的java代码,即服务、dao和一堆模型对象。我希望我可以重写成clojure,这样可以动态生成模型对象(从而大大减少代码大小)。从客户机的角度来看,代码与原始java代码没有什么不同。我建议您从客户机java代码的示例开始,然后尝试找到一个通用接口,该接口可以描述您需要的所有情况,并且可以从Clojure中
具体化
。问题实际上在于,在没有与Java方签订通用合同的情况下,试图动态生成JavaBean。