^ char类型提示不允许clojure defn参数
观察以下repl会话:
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只支持数字原语的长或双类型提示,并且Java char
是数字数据类型 - 不需要解释这一点。 但上面看起来不一致 - 在for
的第一个函数中允许使用类型提示^char
,但不能在blah-c
的函数签名中使用,这里我必须指定Character
。 这是什么原因(即从编译器实现的角度来看)?
的type-暗示for
您正在标记表达c
作为char
作为提示给编译器。 当编译器发出isDigit
的(静态)方法时,它知道你想要接受一个char
的版本(而不是int
版本)。 字节码被发送到实现IFn
接口的O
(单个Object
参数)版本的函数对象中(所有参数默认都是装箱的)。
在另一种情况下, blah-c
,需要将字节码发送给实现IFn
接口不存在的C
(例如, char
)版本的函数对象。 可以有每个原始的接口吗? 当然,但没有。 对于每种可能的组合? 由于组合爆炸,不可行。
你可以说,那么,为什么不直接将blah-c
发送到O
接口呢? 这将打破函数参数的类型提示点,这是为了避免装箱/取消装箱,因为字符原语必须被装箱才能进行调用。 函数参数的类型提示点不仅仅是为了避免反射。 如果你想在这里避免反射,那么你不会标记函数参数,而是在进行isDigit
调用之前将它强制转换为let
块中的char
。
请注意,clojure.lang.IFn中枚举的接口(当前)仅限于任意数量的对象(盒装类型)以及最多四种double
和long
精度类型的组合。 double
版本和long
版本作为优化提供,以避免在基元上编写性能关键代码时进行装箱/取消装箱,并且应该足以满足大多数目的。
这是基于@A的评论。 Webb和@kotarak,据我了解他们。
这有两个方面:首先,为什么^char
在某些情况下可用(例如for
)? 这不仅是优化所必需的,而且正如您的示例所示,正确的Java互操作。 另外,它看起来(对我来说)实施相对便宜,因为每个变量都是独立的,所以它可以自行处理。
对于函数定义,情况并非如此,对于支持类型的每种组合,您必须定义一个新接口:例如,
static public interface L{long invokePrim();}
static public interface D{double invokePrim();}
static public interface OL{long invokePrim(Object arg0);}
// ...
static public interface OLD{double invokePrim(Object arg0, long arg1);}
// all the way to
static public interface DDDDD{double invokePrim(double arg0, double arg1, double arg2, double arg3);}
每种新的支持类型都会增加许多新的接口(指数增长)。 这就是为什么只支持最广泛的原始类型: long
和double
。
上一篇: ^char type hint not permitted for clojure defn parameter