调用协议伪装成通用超类/接口的函数?

时间:2013-10-23 14:18:53

标签: clojure

如,

(defprotocol P
  (foo [x])
  (bar [x]))


(extend-protocol P

  Superclass ;; a java abstract class
  (foo [x]
    (println "superclass"))

  Subclass ;; a concrete java class implementing the above abstract class
  (foo [x]
    (foo (cast Superclass x))))

如果致电

(foo subclass-instance)

显然我会得到一个堆栈溢出,但有没有办法完成我在这里尝试做的事情,即调用相同的函数但伪装成通用超类/接口?

更新:一个更清晰的示例,演示了我要问的用例:

(defprotocol P
  (extract-properties-into-map [self]))

(extend-protocol P
  PropertyContainer  ;; abstract class
  (extract-properties-into-map
    [this]
    (into {} (for [[k v] (.getProperties this)] [(keyword k) (read-string v)])))

  Foo
  (extract-properties-into-map
    [this]
    (assoc {:properties (extract-properties-into-map
                          ^PropertyContainer this)} ;; this is where it falls apart
      :foo-type (.getFooType this)
      :foo-permissions (.getPermissions this)
      :foo-whatever (.getWhatever this))))

2 个答案:

答案 0 :(得分:0)

cast的问题在于它的作用类似于type assertion,只是在对象不满足is-a关系的情况下抛出异常。

(defn cast
  "Throws a ClassCastException if x is not a c, else returns x."
  {:added "1.0"
   :static true}
  [^Class c x] 
  (. c (cast x)))

没有新接口被返回到另一个函数中调度,即你有一个堆栈溢出。

我不确定为Interface扩展协议意味着什么?既然你提供了一个实现,我想你应该首先定义一个type并在超类型上扩展协议。

答案 1 :(得分:0)

编辑:根据https://gist.github.com/michalmarczyk/1715851

中的delegating-proxy得出的更好的答案
(defprotocol P
   (foo [x])
   (bar [x]))


(extend-protocol P

  Number ;; a java abstract class
  (foo [x]
      (println "superclass:" x))

  Integer ;; a concrete java class implementing the above abstract class
   (foo [x]
    (foo (delegating-proxy x [Number] []))))

调用
(foo (Integer. 1))
=> superclass: 1

虽然它问了问题,现在它包裹了原来的x。根据要求,将foo委托给协议中未包含的函数可能更好,可能superfoo

(defn superfoo [x] { :byte (.byteValue x) })

(defprotocol P
  (foo [x])
  (bar [x]))


(extend-protocol P

  Number ;; a java abstract class
   (foo [x]
     (superfoo x))

  Integer ;; a concrete java class implementing the above abstract class
    (foo [x]
      (merge (superfoo x) { :f (.floatValue x)})))

我认为潜在的问题是协议不了解类继承。此外,似乎Clojure应该等待将对象强制转换为类型。在这种情况下,类型提示不起作用。