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))