关于clojure:extend-protocol的第二个参数有什么用

What's the use of the second argument on extend-protocol

extend-protocol上的第三个参数的用途是什么?假设我有

1
2
3
4
5
6
7
8
(defprotocol my-protocol
  (foo [x]))

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

(foo"fooooo") ; 6

已转换为:

1
2
3
4
5
6
7
8
9
(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()


如果将String更改为Long,则在调用带有较长字符的foo时会出现异常:

1
2
3
;; 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

1
2
3
;; 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-protocol扩展为String,然后以单独的extend-protocol形式再次将其扩展为Long,则foo将对字符串有效。

Clojure不会尝试静态地确定(.length x)是否在给定扩展协议类型的情况下有意义。我想在这种情况下是可以的,因为String是最终类,但在一般情况下(非最终类/接口)不是-或至少没有改变其动态语义。