【问题标题】:How to override a method of an existing object?如何覆盖现有对象的方法?
【发布时间】:2016-10-20 08:41:43
【问题描述】:

该对象是用reify 创建的,我需要重写它的一个方法。我发现的唯一方法是使用经典的 OO 装饰器和 reify 的另一种用法。有没有其他办法?

【问题讨论】:

    标签: clojure decorator reify


    【解决方案1】:

    恐怕你必须做一个装饰器,因为 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 完成。

    【讨论】:

    • 谢谢。它只有一个对象,总共有 3 个方法,实现了 2 个协议(一个协议的 2 个方法和另一个协议的 1 个),所以没有宏也不算太糟糕。我只是想确保我没有遗漏核心 Clojure 库中的任何内容。如果方法的数量更大,我肯定会选择宏。该对象是在外部库中创建的,我只需要稍微自定义一下这个对象。
    猜你喜欢
    • 2013-04-13
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多