如何覆盖现有对象的方法?

时间:2016-06-18 17:21:17

标签: clojure decorator reify

该对象是使用reify创建的,我需要覆盖其中一个方法。我找到的唯一方法是使用经典的OO装饰器和reify的另一种用法。还有其他办法吗?

1 个答案:

答案 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完成。