Types Long的Clojure重载方法解析

Types Long的Clojure重载方法解析,types,clojure,Types,Clojure,这种行为对我来说毫无意义: user=> (type 1) java.lang.Long user=> (type (cast Long 1)) java.lang.Long user=> (type 1) java.lang.Long user=> (type (Long. 1)) java.lang.Long user=> (type (cast Long 1)) java.lang.Long user=> (BigDecimal. 1) 1M user=

这种行为对我来说毫无意义:

user=> (type 1)
java.lang.Long
user=> (type (cast Long 1))
java.lang.Long
user=> (type 1)
java.lang.Long
user=> (type (Long. 1))
java.lang.Long
user=> (type (cast Long 1))
java.lang.Long
user=> (BigDecimal. 1)
1M
user=> (BigDecimal. (Long. 1))
CompilerException java.lang.IllegalArgumentException: More than one matching method found: java.math.BigDecimal, compiling:(NO_SOURCE_PATH:22) 
user=> (BigDecimal. (cast Long 1))
1M
为什么
(BigDecimal.(Long.1))
案例找不到明确的匹配方法签名,而具有完全相同参数类型的其他两个表达式却成功了


更新: 对于这种行为,我发现更奇怪的是,它似乎对
Long
类型特别:

user=> (BigDecimal. (Long. 1))
CompilerException java.lang.IllegalArgumentException: More than one matching method found: java.math.BigDecimal, compiling:(NO_SOURCE_PATH:1) 
user=> (BigDecimal. (Integer. 1))
1M
从这个角度看,您似乎遇到了Rich Hickey做出的设计决策。具体来说,由于BigDecimal没有签名的构造函数
BigDecimal(Long-Long)
(编辑:,因此编译器必须在
int
Long
构造函数之间进行选择-有关使用
Integer
的原因,请参阅下面的注释。),编译器不会尝试“猜测”您将使用的构造函数,并且显式失败

底线是Java端的特定类型要求需要显式装箱,以获得正确且不易损坏的代码。-里奇·希基

请注意,根据以下说明,文本被解析为基本体,而不是“装箱”类型:

与以前版本的Clojure不同,数字文本被解析为基本长或双精度

要理解其他操作为何工作,必须深入研究Clojure源代码,特别是
Compiler.java
及其内部类。这就是文本自动装箱到
Long
的地方,编译器依次调用
Object.getClass()
(这是
type
class
都做的)

在中,Clojure编译器尝试解析要使用的
BigDecimal
的构造函数。但是,您已明确指定参数的类型为
Long
——没有采用该类型的BigDecimal构造函数

也许这不是“常识”,但Rich Hickey决定,您需要精确地确定参数的类型,并且它们必须与Java类的类型相匹配。编译器拒绝猜测您的意图

注意以下几点:

user=> (new BigDecimal 1M)
Reflection warning, NO_SOURCE_PATH:33 - call to java.math.BigDecimal ctor can't be resolved.
IllegalArgumentException No matching ctor found for class java.math.BigDecimal  clojure.lang.Reflector.invokeConstructor (Reflector.java:183)
还要注意,此Java代码是有效的,并解析为BigDecimal的
int
构造函数:

    byte b = 1;
    new BigDecimal(new Byte(b));
但此代码也会失败(即使它“应该”使用
int
构造函数):

tl;dr:Clojure支持Java互操作,但这并不意味着它必须遵循Java语言规范的升级规则

演员阵容如何??

下面的评论询问有关
(cast)
。在这种情况下,您显式地告诉Clojure编译器将类型解析委托给JVM。请注意以下(无意义的)编译代码,但在运行时失败:

user=> (set! *warn-on-reflection* true)
true
user=> (defn make-big-dec [obj] (BigDecimal. (cast Math obj)))
Reflection warning, NO_SOURCE_PATH:7 - call to java.math.BigDecimal ctor can't be resolved.
#'user/make-big-dec
user=> (make-big-dec 1)
ClassCastException   java.lang.Class.cast (Class.java:2990)
结语II

Clojure社区对此话题进行了不少讨论。请查看这些详细的线程:

(里奇·希基)


(Nathan Marz)

BigDecimal很久没有构造函数了

BigDecimal(BigInteger val)

BigDecimal(BigInteger unscaledVal,整数刻度)

BigDecimal(双值)

BigDecimal(字符串值)

不清楚这些
(Long.1)
中的哪一个匹配。
clojure.core.bigdec
函数通过将输入传递给
bigdec/valueOf
来创建bigdecime,从而对该输入进行处理

core>  (bigdec (Long. 1))
1M
使用此呼叫:

(BigDecimal/valueOf (long x))

有趣的是,强制强制执行是有效的:
(BigDecimal.(long(long.1))
注意,它并没有找不到匹配的方法签名。它找到多个,因此调用不明确。。。至少它是这么说的。真的很有趣。@Tom-当我说找不到匹配项时,我的意思是找不到明确的匹配项。我已经更新了问题谢谢!。这很有趣,但它没有解释为什么
(BigDecimal.(Long.1))
失败,但
(BigDecimal.(cast Long 1))
工作。如果运行
(type(Long.1))
(type(cast Long 1))
它们都将类型报告为
java.lang.Long
。为什么一个有效而另一个无效?好问题<代码>(cast)是一个运行时操作。Clojure不知道返回的对象类型是什么。它将委托给Java反射(请参见
Object.cast()
)。因此,编译可以工作,但反射警告“调用java.math.BigDecimal-ctor无法解析”。这就像java代码中的“硬转换”。现在,您已经强制代码成功编译,但谁知道运行时会发生什么呢?这是一个有趣的答案,但它没有解释为什么Clojure会阻止将
BigDecimal
构造函数应用于
Long
而不是
Integer
。如果它真的只是装箱/取消装箱的问题,那么我希望它在装箱的
Integer
类型上也会失败。我已经用这个例子更新了我的问题。请看一下
Reflector.java
方法。他们决定您的
Long
可以匹配
int
Long
的构造函数,但您的
整数只能匹配
int
的构造函数。请记住,问题是Clojure找到了两个可能的构造函数。如果指定
整数
,则情况并非如此。您可能想提出一个新问题(在这里或邮件列表上),询问Clojure对类型的立场,以及为什么
int
不能是
长的
core> (BigDecimal. BigInteger/ONE 1)
0.1M
core> (BigDecimal. (double 1))
1M
core> (BigDecimal. (float 1))
1M
(BigDecimal. Double/MIN_VALUE)
core> (BigDecimal. "1")
1M
core>  (bigdec (Long. 1))
1M
(BigDecimal/valueOf (long x))