Clojure重载了Longs的方法解析

时间:2012-09-25 16:08:00

标签: 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=> (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

2 个答案:

答案 0 :(得分:11)

this discussion on the Clojure discussion group开始,您似乎遇到了Rich Hickey做出的设计决定。具体来说,因为BigDecimal没有签名的构造函数BigDecimal(Long long) (编辑:,因此让编译器必须在intlong构造函数之间进行选择 - 有关使用Integer的原因的讨论,请参阅下面的评论,编译器不会尝试“猜测”您将使用哪个构造函数,并且明确失败。

  

底线是Java端需要显式装箱的特定类型要求,以便拥有正确且非脆弱的代码。 - Rich Hickey

请注意,根据this documentation,文字被解析为基元,而不是“盒装”类型:

  

与之前版本的Clojure相比,数字文字被解析为原始long或double。

要理解其他操作的工作原理,您必须深入研究Clojure源代码,特别是Compiler.java及其内部类NumberExpr。这就是你的文字被自动装箱到Long的位置,编译器没有问题依次调用Object.getClass()typeclass都这样做)。

Compiler.getMatchingParams()中,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构造函数):

user=> (BigDecimal. (Byte. (byte 1)))
Reflection warning, NO_SOURCE_PATH:37 - 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)

tl; dr:Clojure支持Java interop ,但这并不意味着它必须遵循Java语言规范的推广规则。

cast怎么样?

下面的评论询问(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)

Epilogue II

Clojure社区对此主题进行了大量讨论。请查看这些详细的主题:

Enhanced Primitive Support(Rich Hickey)

Clojure 1.3 treatment of integers and longs(Nathan Marz)

答案 1 :(得分:1)

BigDecimal没有Long的构造函数,

BigDecimal(BigInteger val)

core> (BigDecimal. (BigInteger/ONE))
1M

BigDecimal(BigInteger unscaledVal,int scale)

core> (BigDecimal. BigInteger/ONE 1)
0.1M

BigDecimal(double val)

core> (BigDecimal. (double 1))
1M
core> (BigDecimal. (float 1))
1M
(BigDecimal. Double/MIN_VALUE)

BigDecimal(String val)

core> (BigDecimal. "1")
1M

目前尚不清楚这些(Long. 1)中的哪一个匹配。 clojure.core.bigdec函数通过将其输入传递给BigDec/valueOf来创建BigDecimal

,对此输入起作用
core>  (bigdec (Long. 1))
1M

使用此电话:

(BigDecimal/valueOf (long x))