简体   繁体   中英

How to override a method of an existing object?

The object was created with reify<\/code> and I need to override one of its method. The only way I found is to use classic OO decorator with another use of reify<\/code> . Is there any other way?

"

I'm afraid you have to make a decorator, because Clojure has no built-in construct to delegate an object's behaviour to another object by default (which I think is called prototypal inheritance).

But that does not mean it has to be tedious - you can use a macro and reflection to automate most of the work. Here's a proof of concept:

(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))))))
       )))

How you would use it:

(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"]

And the expansion:

(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))))))

This implementation generates a proxy clause, but it could also be done with reify .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM