观察以下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 only supports long or double type hints for numeric primitives以及Java char
是数字数据类型 - 无需解释。但上面似乎不一致 - 在^char
内的第一个函数中允许使用类型提示for
,但在blah-c
的函数签名中不允许,我必须指定Character
。这是什么原因(即从编译器实现的角度来看)?
答案 0 :(得分:3)
在类型提示的for
表达式中,您将c
标记为char
作为编译器的提示。当编译器为isDigit
发出(静态)方法时,它就知道您希望该版本接受char
(而不是int
版本)。字节代码被发送到实现O
接口的Object
(单IFn
参数)版本的函数对象中(默认情况下,所有参数都被装箱)。
在另一种情况下,blah-c
,需要将字节代码发送到实现不存在的C
(例如,char
)版本的函数对象。 IFn
界面。每个原语都有接口吗?当然,但没有。对于每种可能的组合?由于组合爆炸,不可行。
你可以说,为什么不向blah-c
接口发出O
?这将破坏函数参数上的类型提示点,这是为了避免装箱/拆箱,因为必须将字符原语装箱以进行调用。函数参数的类型提示点不仅仅是为了避免反射。如果你想在这里避免反射,那么你不会标记函数参数,而是在进行char
调用之前将其强制转换为let
块中的isDigit
。
在clojure.lang.IFn中注意,枚举接口(当前)仅限于任意数量的对象(盒装类型)和最多四种double
和long
类型的组合。 double
和long
版本作为优化提供,以避免在基元上编写性能关键代码时装箱/拆箱,并且应该足以满足大多数用途。
答案 1 :(得分:0)
这是基于@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
。