有没有办法在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的想法。
答案 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
类型,而非String
或Integer
等等(您通常也不会在Clojure中声明类型)。它们通常用于地图(使用{}语法)来检索值,例如,以下代码从地图:key2
中检索与{:key1 "hello", :key2 4}
相关联的值。
(get {:key1 "hello", :key2 4} :key2)
4
我不确定您的示例是否试图说明:key1
与String
值相关联,而:key2
与{相关联{1}} 值或者您认为Integer
的类型为:key1
。如果是前者,可能想要使用地图而不是矢量。
我担心我不会认为我对Java bean或您的用例有足够的了解,以便进一步提供帮助。