Java转换:是编译器错误,还是语言规范错误,或者我错了?

时间:2011-03-22 01:28:09

标签: java javac jls

我一直在阅读Java语言规范第3版,并且发现了我认为规范和javac编译器实现之间的差异。 Eclipse编译器中存在相同的差异。

15.16节讨论了强制转换表达式。如果参数类型无法通过强制转换转换为强制类型,那么它应该是一个编译时错误:

  

如果操作数的编译时类型永远不会根据强制转换规则(第5.5节)强制转换为强制转换运算符指定的类型,那么这是一个编译时错误。否则,在运行时,通过将转换转换为强制转换操作符指定的类型来转换操作数值(如果需要)。

5.5节讨论了转换。它给出了允许的转换类型列表。列表中特别缺少的是“取消装箱转换,然后加宽/缩小原始转换”。 然而 javac编译器(以及Eclipse编译器)似乎确实允许确切的转换序列。例如:

long l = (long) Integer.valueOf(45);

...编译得很好。 (有问题的演员阵容是对long的强制转换;参数的类型为java.lang.Integer,因此转换需要取消装箱到int,然后进行加宽的原始转换。

同样,根据JLS,不应该从byte转换为char,因为(根据5.1.4)需要扩展原始转换和< / em>缩小的原始转换 - 但是,编译器也允许这种强制转换。

任何人都可以启发我吗?

编辑:自问这个以来,我已经向Oracle提交了bug report。他们的回答是,这是“JLS中的一个小故障”。

2 个答案:

答案 0 :(得分:3)

我认为你是对的,编译器是正确的,规范是错误的......

这会编译:{{1​​}},但不会:(Object)45

理解编译器的行为(包括我正在使用的Intellij)的唯一方法是修改Casting Conversion以同意分配转换和方法调用转换:

  • 拳击转换(§5.1.7) 随后加宽 参考转换

  • 拆箱转换(§5.1.8) 随后可选择加宽 原始转换。

  • 扩大和缩小原始概念

规范确实说“转换转换比分配或方法调用转换更具包容性:转换可以执行除字符串转换或捕获转换之外的任何允许转换”

答案 1 :(得分:1)

通过我的阅读,本条允许从intlong的演员阵容:

  

如果类型相同,或者通过加宽基元转换或缩小基元转换,可以通过标识转换将基本类型的值转换为其他基本类型。

int转换为long扩展基元转换

刚刚离开Integerint的转换,这是由最后一个子弹提供的:

  

取消装箱转化

当然,在示例中甚至不需要转换为long

考虑以下四个定义:

final Integer io = Integer.valueOf(45);
final int i = io;
final long l1 = (long)i;
final long l2 = i;

你认为他们中的任何一个都令人惊讶吗?你原来的例子看起来没什么不同;它只是省略了中间变量。