为此,我会结合多态性进行编译时反射。
(defprotocol FieldSettable (set-field! [this k v]))
(defmacro extend-field-setter [klass]
(let [obj (with-meta (gensym "obj_") {:tag klass})
fields (map #(symbol (.getName ^java.lang.reflect.Field %))
(-> klass str (java.lang.Class/forName) .getFields))
setter (fn [fld]
`(fn [~obj v#] (set! (. ~obj ~fld) v#) ~obj))]
`(let [m# ~(into {} (map (juxt keyword setter) fields))]
(extend ~klass
FieldSettable
{:set-field! (fn [~obj k# v#] ((m# k#) ~obj v#))}))))
这允许您扩展每个类的字段设置器。
(extend-field-setter java.awt.Point)
(extend-field-setter java.awt.Rectangle)
现在set-field!
可以在其中任何一个上使用,并且可以reduce-kv
在地图上使用。
(def pt (java.awt.Point.))
(def rect (java.awt.Rectangle.))
(def m {:x 1, :y 2})
(reduce-kv set-field! pt m)
;=> #<Point java.awt.Point[x=1,y=2]>
(reduce-kv set-field! rect m)
;=> #<Rectangle java.awt.Rectangle[x=1,y=2,width=0,height=0]>
由于未在地图中指定,因此在rect
示例中的位置width
和height
字段保持不变。