JVM Clojure的Protcols实现如何工作?

Ale*_*x D 1 java interface clojure

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

Ale*_*x D 5

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

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

这似乎意味着当扩展到任意类时,协议方法调用应该比普通的Clojure函数调用慢.与reify/ deftype/ 一起使用时的性能defrecord应该更好,如果你内联扩展协议,而不是单独调用extend-protocolextend-type.