在我看来,协议的一个主要优点是协议方法具有正确的命名空间限定。因此,我可以拥有多个不同命名空间中的协议,这些协议都定义了一个名为foo的方法,并可以在现有的类型上扩展它们(或它们的子集)。然而,使用defrecord和deftype内联扩展协议,或在Java端通过实现它们的接口来扩展协议并不是这样。
示例
`
;; 文件: protocoltest/foo.clj
(ns prototest.foo)
(defprotocol Foo
(mymethod [this]))
;; 文件: protocoltest/bar.clj
(ns prototest.bar)
(defprotocol Bar
(mymethod [this]))
;; 文件: protocoltest/core.clj
(ns prototest.core
(:require [prototest.foo :as foo]
[prototest.bar :as bar]))
;; 内联扩展两个mymethod方法不起作用
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo)
bar/Bar
(mymethod [this] :bar))
;;=> java.lang.ClassFormatError
;; 类文件prototest/core/MyRec中重复了方法名称和签名
;; 我不得不求助于部分内联和部分动态...
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo))
(extend-type MyRec
bar/Bar
(mymethod [this] :bar))
;; ... 或者完全动态扩展。
(defrecord MyRec [x])
(extend-type MyRec
foo/Foo
(mymethod [this] :foo)
bar/Bar
(mymethod [this] :bar))
;; 然后一切正常运行。
(foo/mymethod (->MyRec 1))
;;=> :foo
(bar/mymethod (->MyRec 1))
;;=> :bar
`
我知道我得到错误是因为支持这两个协议的Foo和Bar接口都包含一个名为mymethod的方法,因此不能同时实现(至少不是具有不同行为的方式)。
但为什么要放弃我们通过协议拥有的命名空间优势呢?例如,为什么protocoltest.foo.Foo方法在相应的接口中不是命名为protocoltest$foo$mymethod(或一些其他混淆的名称)?这样的话,两种方法都可以在内联中实现,从而获得速度优势,您还可以从Java端实现相同的操作。(当前,使用clojure.java.api通过Java端调用clojure.core.extend并不是很有趣,因为您必须构造映射、内部关键字、定义函数等。)
当然,更改默认方法命名方案的船只早已离港,但也许可以在defprotocol中添加一个:ns-qualified-method-names选项。