Clojure协议的文档指出,每个协议都会生成一个对应的 Java 接口。然而,你可以用协议做的事情(将它们扩展到任意类型等)看起来不像任何在 Java 接口方面具有直接实现的东西。协议和协议方法如何在内部工作?为什么以及如何需要每个协议的 Java 接口?
问问题
179 次
1 回答
6
对 Clojure 平台源代码的简要检查表明:
- 对于每个协议方法,都有一个从 Java 映射
Class
到实现该协议的 Clojure 函数的哈希表Class
。(它实际上是一个名为 的自定义类的实例MethodImplCache
。) - 当调用协议方法的调度函数时,它会获取
Class
第一个参数并在哈希表中进行查找。 - 如果没有找到任何东西,它会遍历第一个参数的继承链,直到找到查找成功的超类。然后将dispatch插入到
Class
hashtable中,这样下次就可以直接找到了。 - 此外,每当查找成功时,最近找到
Class
的及其对应的方法实现都会缓存在实例变量中,如果Class
下次调度相同,则用于绕过哈希查找。 - 当协议扩展到任意 Java 类时,生成的接口不会以任何方式发挥作用。对协议方法的调用
MethodImplCache
按照上述方法进行解析。 - 当您用于获取扩展协议的匿名类的实例,或者如果您使用或创建扩展协议的新类时,将使用生成的接口。在任何一种情况下,您都会得到一个实现生成接口的对象。
reify
deftype
defrecord
Class
- 当 Clojure 编译器为调用协议方法发出 JVM 字节码时,它会插入一些字节码来检查第一个参数是否是生成接口的实例。如果是,它使用普通的 Java 方法调用接口的适当方法。在这种情况下,我上面描述的协议调度函数被完全绕过并且永远不会被调用。
这一切似乎都意味着,当扩展到任意类时,协议方法调用应该比普通的 Clojure 函数调用慢。如果您扩展协议内联,而不是单独调用or来这样做,与reify
//一起使用时的性能应该会更好。deftype
defrecord
extend-protocol
extend-type
于 2013-09-02T16:19:29.853 回答