我一直在阅读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中的一个小故障”。
答案 0 :(得分:3)
我认为你是对的,编译器是正确的,规范是错误的......
这会编译:{{1}},但不会:(Object)45
理解编译器的行为(包括我正在使用的Intellij)的唯一方法是修改Casting Conversion以同意分配转换和方法调用转换:
拳击转换(§5.1.7) 随后加宽 参考转换
拆箱转换(§5.1.8) 随后可选择加宽 原始转换。
加
规范确实说“转换转换比分配或方法调用转换更具包容性:转换可以执行除字符串转换或捕获转换之外的任何允许转换”
答案 1 :(得分:1)
通过我的阅读,本条允许从int
到long
的演员阵容:
如果类型相同,或者通过加宽基元转换或缩小基元转换,可以通过标识转换将基本类型的值转换为其他基本类型。
将int
转换为long
是扩展基元转换。
刚刚离开Integer
到int
的转换,这是由最后一个子弹提供的:
取消装箱转化
当然,在示例中甚至不需要转换为long
。
考虑以下四个定义:
final Integer io = Integer.valueOf(45);
final int i = io;
final long l1 = (long)i;
final long l2 = i;
你认为他们中的任何一个都令人惊讶吗?你原来的例子看起来没什么不同;它只是省略了中间变量。