8

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

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
4

2 回答 2

11

Clojure 讨论组的这个讨论中,您似乎遇到了 Rich Hickey 做出的设计决策。具体来说,因为 BigDecimal 没有签名的构造函数BigDecimal(Long long) (编辑:因此让编译器不得不在intlong构造函数之间进行选择 - 请参阅下面的评论以讨论为什么 usingInteger有效),编译器不会尝试“猜测”哪个构造函数你的意思是,并且明确地失败了。

底线是 Java 端的特定类型要求需要显式装箱才能获得正确且不易碎的代码。- 丰富的希基

请注意,根据本文档,文字被解析为原语,而不是“盒装”类型:

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

要了解其他操作为何起作用,您必须深入研究 Clojure 源代码,特别是Compiler.java它的内部类NumberExpr。这就是你的文字被自动装箱到 aLong并且编译器依次调用没有问题的地方Object.getClass()(两者都type这样class做)。

在 中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 代码有效并解析int为 BigDecimal 的构造函数:

    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互操作,但这并不意味着它必须遵循 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)

结语二

在 Clojure 社区中,关于这个主题的讨论相当多。请查看这些详细的线程:

增强的原始支持(Rich Hickey)

Clojure 1.3 处理整数和长整数(Nathan Marz)

于 2012-09-25T19:00:13.050 回答
1

BigDecimal 没有 Long 的构造函数,

BigDecimal(BigInteger val)

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

BigDecimal(BigInteger unscaledVal, int scale)

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

BigDecimal(双值)

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

BigDecimal(字符串值)

core> (BigDecimal. "1")
1M

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

core>  (bigdec (Long. 1))
1M

使用此调用:

(BigDecimal/valueOf (long x))
于 2012-09-25T19:06:47.527 回答