2011-01-29 61 views
14

在定義了一條記錄及其實現的接口之後,我可以通過它的名稱或使用點運算符的java interop方法來調用它的方法。clojure的defrecord方法名稱解析如何工作?

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方法,以便您不需要。運營商,以便行爲如上? (例如統一用戶界面的目的)

+0

我的文體偏好是避免Java int的的兩個直列(它們中的至少一個必須是在extend-*形式),記錄(構造函數)和協議(調用)的erop形式。在這種情況下:(吃( - >動物「鮑勃」)) – 2014-07-15 17:16:26

回答

23

定義一個協議時,它的每個方法都是作爲當前命名空間中的函數創建的。因此,你不能有兩個協議在相同的命名空間中定義相同的功能。這也意味着你可以將它們放在單獨的名稱空間中,並且給定的類型可以在沒有任何nameclash的情況下擴展它們[1],因爲它們是命名空間的(與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]但是不能擴展,因爲一個實現限制

+0

謝謝,這清理了很多。 – bmillare 2011-01-30 19:11:10