Clojure重载了Longs的方法解析

Dao*_*Wen 8 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
Run Code Online (Sandbox Code Playgroud)

为什么这个(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
Run Code Online (Sandbox Code Playgroud)

noa*_*hlz 11

上Clojure的讨论组的讨论,似乎你也遇到了由Rich希基做出设计决策.具体来说,因为BigDecimal的没有签名的构造函数BigDecimal(Long long) (编辑:因此留下具有之间进行选择的编译器intlong构造-见下面的讨论意见,为什么使用Integer作品),编译器不会试图"猜"的构造你的意思,并明确失败.

底线是Java方面的特定类型要求需要显式装箱才能拥有正确且非脆弱的代码. - Rich Hickey

请注意,根据此文档,文字被解析为基元,而不是"盒装"类型:

与之前版本的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)
Run Code Online (Sandbox Code Playgroud)

另请注意,此Java代码有效并解析int为BigDecimal 的构造函数:

    byte b = 1;
    new BigDecimal(new Byte(b));
Run Code Online (Sandbox Code Playgroud)

但是这段代码也失败了(即使它"应该"使用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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

结语II

在Clojure社区中对此主题进行了相当多的讨论.请查看这些详细的主题:

增强的原始支持(Rich Hickey)

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

  • 好问题.`(cast)`是一个运行时操作.Clojure不知道返回的对象类型是什么.它委托给Java反射(参见`Object.cast()`).因此,编译工作,但用反射警告"调用java.math.BigDecimal ctor无法解决".这就像Java代码中的"强制转换"一样.现在你已经强制代码成功编译了,但谁知道在运行时会发生什么? (3认同)