2014-02-20 54 views
2

第三個參數在extend-protocol上有什麼用處?假設我有擴展協議第二個參數有什麼用處

(defprotocol my-protocol 
    (foo [x])) 

(extend-protocol my-protocol 
    java.lang.String ; this 
    (foo [x] (.length x))) 

(foo "fooooo") ; 6 

轉化到:

(defprotocol my-protocol 
    (foo [x])) 

(extend-protocol my-protocol 
    java.lang.Long ; this 
    (foo [x] (.length x))) 

(foo "fooooo") ; it gave an output 6, while I expect it will throws, since I'm extending from Long, where it doesn't have length() 
       ; In the end, it will just check on the args ? (in this case it's x) 

在那裏,我把它java.lang.String,如果我將其更改爲java.lang.Long,呼籲foo沒有拋出任何異常,而Long沒有length()上它。它引發的唯一情況是當參數foo沒有length()時。

+0

正如我在回答中所提到的,如果將協議擴展爲'String'和*然後*到'Long',則實現到'String'不會消失。所以這就是爲什麼'foo'仍然在字符串上工作。但是現在你也有一個'Long'的實現是沒有意義的,所以如果你長時間調用'foo',而不是告訴你沒有匹配的實現,Clojure會嘗試使用你提供的那個,在做這件事時遇到問題。 –

回答

2

如果更改StringLong,那麼你將有一個長調用foo時得到一個異常:

;; calling foo with a Long: 
user=> (foo 1) 
IllegalArgumentException No matching field found: length for class java.lang.Long clojure.lang.Reflector.getInstanceField (Reflector.java:271) 

如果你沒有將協議擴大到String,你將不能夠呼籲foo對字符串:

;; calling foo with a String with no implementation provided: 
user=> (foo "asdf") 
IllegalArgumentException No implementation of method: :foo of protocol: #'user/my-protocol found for class: java.lang.String clojure.core/-cache-protocol-fn (core_deftype.clj:537) 

當然如果你擴展my-protocolString第一,然後,在一個單獨的extend-protocol形式,前再次傾向於Long,然後foo將正常工作與字符串。

Clojure沒有嘗試靜態確定(.length x)是否合理,因爲您正在擴展協議的類型。我認爲它可以在這種情況下,因爲String是一個最終的類,但不是在一般情況下(非最終類/接口) - 或至少不是沒有改變其動態語義。

+1

我想OP會奇怪爲什麼在'x'上似乎沒有隱式類型標記,即爲什麼在編譯時沒有拋出異常,儘管'Long'沒有方法'length'。有關於此的任何想法? (哈,你在我打字的時候回答了,不要緊,然後......)) – xsc

+0

:-)實際上''x''實際上是隱式類型暗示的(對協議被擴展到的類型),但類型提示只是用於避免反射。 –

+0

所以這只是爲了抽象,是對的嗎? – user3331731