该对象是使用reify
创建的,我需要覆盖其中一个方法。我找到的唯一方法是使用经典的OO装饰器和reify
的另一种用法。还有其他办法吗?
答案 0 :(得分:4)
我担心你必须做一个装饰器,因为Clojure没有内置的构造来默认将对象的行为委托给另一个对象(我认为它被称为原型继承)。
但这并不意味着它必须单调乏味 - 您可以使用宏和反射来自动完成大部分工作。这是一个概念证明:
(defmacro decorator
[clazz proto & fs]
(let [proto-name (gensym "proto")
methods (->> (clojure.reflect/reflect (resolve clazz))
:members
(filter #(instance? clojure.reflect.Method %))
(map (fn [{:keys [name parameter-types]}]
[name (count parameter-types)]))
set)
to-delegate (clojure.set/difference
methods
(->> fs
(map (fn [[name params]]
[name (count params)]))
set))
method-bodies
(concat
fs ;; these are our own definitions
(for [[name n-params] to-delegate]
(let [params (->> (range n-params)
(map #(gensym (str "x" %))))]
`(~name [~@params]
(. ~proto-name (~name ~@params))) ;; this is where we delegate to the prototype
)))]
`(let [~proto-name ~proto]
(proxy
[~clazz] []
~@(->> method-bodies (group-by first) (sort-by first)
(map (fn [[name bodies]]
`(~name ~@(for [[name & rest] bodies]
rest))))))
)))
你将如何使用它:
(decorator
java.util.Collection
[:a :b :c]
(size [] -1))
=> #object[user.proxy$java.lang.Object$Collection$4e41253d
0x1eae8922
"user.proxy$java.lang.Object$Collection$4e41253d@6abe9887"]
扩张:
(macroexpand-1 '(decorator
java.util.Collection
[:a :b :c]
(size [] -1)))
=>
(clojure.core/let
[proto28109 [:a :b :c]]
(clojure.core/proxy
[java.util.Collection]
[]
(add ([x028114] (. proto28109 (add x028114))))
(addAll ([x028110] (. proto28109 (addAll x028110))))
(clear ([] (. proto28109 (clear))))
(contains ([x028118] (. proto28109 (contains x028118))))
(containsAll ([x028116] (. proto28109 (containsAll x028116))))
(equals ([x028119] (. proto28109 (equals x028119))))
(hashCode ([] (. proto28109 (hashCode))))
(isEmpty ([] (. proto28109 (isEmpty))))
(iterator ([] (. proto28109 (iterator))))
(parallelStream ([] (. proto28109 (parallelStream))))
(remove ([x028117] (. proto28109 (remove x028117))))
(removeAll ([x028115] (. proto28109 (removeAll x028115))))
(removeIf ([x028111] (. proto28109 (removeIf x028111))))
(retainAll ([x028112] (. proto28109 (retainAll x028112))))
(size ([] -1))
(spliterator ([] (. proto28109 (spliterator))))
(stream ([] (. proto28109 (stream))))
(toArray ([] (. proto28109 (toArray))) ([x028113] (. proto28109 (toArray x028113))))))
此实现生成proxy
子句,但也可以使用reify
完成。