观察以下 repl session :
user=> (set! *warn-on-reflection* true)
true
user=> (defn blah [s] (for [c s] (if (Character/isDigit c) true false)))
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
#'user/blah
user=> (blah "abc123abc")
(false false false true true true false false false)
user=> (defn blah [s] (for [^char c s] (if (Character/isDigit c) true false)))
#'user/blah
user=> (blah "abc123abc")
(false false false true true true false false false)
因此我们使用了 ^char
的类型提示来摆脱反射 - 太棒了。现在在函数参数中尝试同样的事情:
user=> (defn blah-c [c] (if (Character/isDigit c) true false))
Reflection warning, NO_SOURCE_PATH:1:22 - call to isDigit can't be resolved.
#'user/blah-c
user=> (defn blah-c [^char c] (if (Character/isDigit c) true false))
CompilerException java.lang.IllegalArgumentException: Only long and double primitives are supported, compiling:(NO_SOURCE_PATH:1:1)
user=> (defn blah-c [^Character c] (if (Character/isDigit c) true false))
#'user/blah-c
user=> (blah-c \1)
true
user=> (blah-c \a)
false
我了解 Clojure only supports long or double type hints for numeric primitives ,并且 Java char
是一种数字数据类型 - 无需解释。但上面似乎不一致 - 在 for
内的第一个函数中允许类型提示 ^char
,但在 blah-c
的函数签名中不允许>,我必须在其中指定Character
。这是什么原因(即从编译器实现的角度来看)?
最佳答案
在类型提示中for
您正在标记的表达式 c
作为char
作为对编译器的提示。当编译器为 isDigit
发出(静态)方法时然后它知道您想要接受 char
的版本(可能与 int
版本相反)。字节码被发送到实现 O
的函数对象中(单个 Object
参数)IFn
的版本接口(interface)(默认情况下所有参数都已装箱)。
在另一种情况下,blah-c
,字节码需要发送到实现不存在的 C
的函数对象。 (例如,对于 char
)IFn
的版本界面。每个原语都可以有接口(interface)吗?当然可以,但是没有。对于每种可能的组合?由于组合爆炸,不可行。
你可能会说,好吧,为什么不直接发出 blah-c
到O
界面?这将违背函数参数上类型提示的要点,即避免装箱/拆箱,因为随后必须对字符基元进行装箱才能进行调用。函数参数的类型提示的目的不仅仅是为了避免反射。如果你想在这里避免反射,那么你不会标记函数参数,而是将其强制转换为 char
在 let
在创建 isDigit
之前阻止打电话。
clojure.lang.IFn中的注释,枚举接口(interface)(当前)仅限于任意数量的对象(盒装类型)以及最多四种 double
的组合。和long
类型。 double
和long
提供版本是为了优化,以避免在基元上编写性能关键代码时装箱/拆箱,并且应该足以满足大多数用途。
关于clojure - clojure defn 参数不允许使用 ^char 类型提示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17323769/