Отнюдь не идеально, и, вероятно, у вас возникает много непредвиденных проблем, когда вы пытаетесь его использовать, но я думаю, вы можете начать с чего-то вроде:
(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. Реализация использует атом для хранения всех свойств, поэтому убедитесь, что вы понимаете компромиссы.
Кроме того, при работе в Clojure вы, вероятно, не хотите работать с javabeans, но вы можете создать несколько методов для получения и установки атома, удерживающего состояние.