clojure的defrecord方法名称解析如何工作?

bmi*_*are 14 protocols clojure

在定义记录及其实现的接口之后,我可以通过其名称或使用点运算符的java互操作方式调用其方法.

 user=> (defprotocol Eat (eat [this]))
 Eat
 user=> (defrecord animal [name] Eat (eat [this] "eating"))
 user.animal
 user=> (eat (animal. "bob"))
 "eating"
 user=> (.eat (animal. "bob"))
 "eating"
 user=> 
Run Code Online (Sandbox Code Playgroud)

在表面下,那里发生了什么?是否定义了新的clojure函数?如果您定义的函数共享相同的名称会发生​​什么(这可能吗?),这些歧义如何解决?

此外,是否可以为其他java对象"导入"java方法,以便您不需要.运营商,以便行为如上?(例如,用于统一用户界面的目的)

cgr*_*and 23

定义协议时,每个方法都在当前名称空间中创建为函数.因此,您不能在同一名称空间中使用两个协议来定义相同的功能.它还意味着你可以将它们放在不同的命名空间中,并且给定的类型可以扩展它们[1]而不需要任何nameclash因为它们是命名空间(与Java相反,其中单个类不能用同名方法实现两个接口) .

从用户的角度来看,协议方法与普通的非多态函数没有区别.

您可以使用interop调用协议方法的事实是一个实现细节.原因是对于每个协议,Clojure编译器都会创建相应的后备接口.稍后,当您定义具有内联协议扩展的新类型时,此类型将实现这些协议的后备接口.

因此,您无法在未提供内联扩展的对象上使用互操作表单:

(defrecord VacuumCleaner [brand model]
(extend-protocol Eat
  VacuumCleaner
  (eat [this] "eating legos and socks"))

(.eat (VaacumCleaner. "Dyson" "DC-20"))
; method not found exception
Run Code Online (Sandbox Code Playgroud)

编译器对协议函数有特殊支持,因此它们被编译为实例检查,后跟虚拟方法调用,因此在适用时(eat ...)将尽可能快(.eat ...).

要回复"可以导入java方法",可以将它们包装在常规的fns中:

(def callme #(.callme %1 %2 %3))
Run Code Online (Sandbox Code Playgroud)

(显然,您可能需要添加其他arities来解决重载并输入提示以删除反射)

[1]但是由于实现限制,您不能同时扩展内联(至少其中一个必须在extend-*表单中)