JVM Clojure协议的实现如何工作?

时间:2013-09-02 16:19:30

标签: java interface clojure

Clojure protocols上的文档声明为每个协议生成了相应的Java接口。但是,你可以用协议做的事情(将它们扩展到任意类型等)看起来不像是在Java接口方面有直接实现的东西。协议和协议方法如何在内部工作?为什么以及如何使用每个协议的Java接口?

1 个答案:

答案 0 :(得分:5)

对Clojure平台源代码的简要介绍揭示:

  1. 对于每种协议方法,都有一个哈希表,它从Java Class映射到Clojure函数,该函数实现了Class的协议。 (它实际上是一个名为MethodImplCache的自定义类的实例。)
  2. 当调用协议方法的调度函数时,它获取第一个参数的Class并在哈希表中进行查找。
  3. 如果没有找到任何内容,它会遍历第一个参数的继承链,直到找到查找成功的超类。然后它在散列表中插入调度Class,以便下次可以直接找到它。
  4. 此外,每次查找成功时,最近发现的Class及其对应的方法实现都缓存在实例变量中,如果调度Class相同,则用于绕过哈希查找下一次。
  5. 当协议扩展到任意Java类时,生成的接口不会以任何方式发挥作用。如上所述,使用MethodImplCache解析对协议方法的调用。
  6. 当您使用reify获取扩展协议的匿名类的实例时,或者使用deftype或{{1}时,会使用生成的接口 创建一个扩展协议的新类。在任何一种情况下,您都将获得实现生成的接口的defrecord对象。
  7. 当Clojure编译器为调用协议方法发出JVM字节码时,它会插入一些字节码,检查第一个参数是否是生成的接口的实例。如果是,它使用普通的Java方法调用接口的相应方法。在这种情况下,上面描述的协议调度功能被完全绕过并且从不被调用。
  8. 这似乎意味着当扩展到任意类时,协议方法调用应该比普通的Clojure函数调用慢。如果您将协议内联扩展,而不是单独调用Class或{{1},那么与reify / deftype / defrecord一起使用时的效果会更好}}