[英]How does clojure's defrecord method name resolution work?
在定义记录及其实现的接口之后,我可以通过其名称或使用点运算符的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=>
在表面下,那里发生了什么? 是否定义了新的clojure函数? 如果您定义的函数共享相同的名称会发生什么(这可能吗?),这些歧义如何解决?
此外,是否可以为其他java对象“导入”java方法,以便您不需要。 运营商,以便行为如上? (例如,用于统一用户界面的目的)
定义协议时,每个方法都在当前名称空间中创建为函数。 因此,您不能在同一名称空间中使用两个协议来定义相同的功能。 它还意味着你可以将它们放在不同的命名空间中,并且给定的类型可以扩展它们[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
编译器对协议函数有特殊支持,因此它们被编译为实例检查,后跟虚拟方法调用,因此适用时(eat ...)
将与(.eat ...)
一样快。
要回复“可以导入java方法”,可以将它们包装在常规的fns中:
(def callme #(.callme %1 %2 %3))
(显然,您可能需要添加其他arities来解决重载并输入提示以删除反射)
[1]但是你不能同时扩展内联 (至少其中一个必须是extend-*
形式),因为实现限制
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.