types - 用于 Longs 的 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=> (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)) case 未能找到明确的匹配方法签名,而其他两个表达式(具有完全相同的参数类型)成功?

更新:

我发现这种行为更奇怪的是它似乎是 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

最佳答案

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

The bottom line is specific type requirements on the Java side require explicit boxing on order to have correct and non-brittle code. - Rich Hickey



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

In contrast to previous versions of Clojure, numeric literals are parsed as primitive longs or doubles.



要了解其他操作为何有效,您必须深入研究 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 代码有效并解析为 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 编译器将类型解析委托(delegate)给 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 社区中,关于这个主题的讨论相当多。请查看这些详细的线程:

Enhanced Primitive Support (丰富的希基)

Clojure 1.3 treatment of integers and longs (内森·马兹)

关于types - 用于 Longs 的 Clojure 重载方法解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12586881/

相关文章:

php - PHP 从 Postgres 正确读取数据类型

ruby - 在 ruby​​ 中柯里化(Currying)一个 varargs proc 并获得另一个 varargs proc

java - 让 Leiningen 在 Mac OS X 10.7.2 上运行

r - 为什么向量由 ':' 整数创建?

c++ - 从函数 C++ 返回文件 ID

c++ - 编译器强制语义类型

macros - clojure defmacro 中的多重性

performance - Clojure 时间序列分析

java - java/clojure 中的单字符控制台输入

C++ 方法调用和类型范围解析歧义