Java转换:是编译器错误,还是语言规范错误,还是我错了?
我一直在阅读Java语言规范第3版,并且发现了我认为规范和javac编译器实现之间的差异。 Eclipse编译器中存在相同的差异。
第15.16节讨论了强制转换表达式。 如果参数类型无法通过强制转换转换为强制类型,那么它应该是一个编译时错误(第5.5节):
如果操作数的编译时类型永远不会根据强制转换规则(第5.5节)强制转换为强制转换运算符指定的类型,那么这是一个编译时错误。 否则,在运行时,通过将转换转换为强制转换运算符指定的类型来转换操作数值(如果需要)。
第5.5节讨论了转换。 它给出了允许的转换类型列表。 列表中特别缺少的是“取消装箱转换,然后加宽/缩小原始转换”。 然而 ,javac编译器(以及Eclipse编译器)似乎确实允许确切的转换序列。 例如:
long l = (long) Integer.valueOf(45);
……编译得很好。 (有问题的强制转换是强制转换;参数的类型为java.lang.Integer
,因此转换需要取消装箱到int
后跟扩展的原始转换)。
同样,根据JLS,不应该从byte
为char
,因为(根据5.1.4 )需要扩展原始转换和缩小基元转换 – 但是,编译器也允许这种转换。
任何人都可以开导我吗?
编辑:自从问这个以来,我已经向Oracle提交了一份错误报告 。 他们的反应是,这是“JLS中的一个小故障”。
我认为你是对的,编译器是正确的,规范是错误的……
这编译:( (Object)45
,这不:( (Long)45
理解编译器行为(包括我正在使用的Intellij)的唯一方法是修改Casting Conversion以同意Assignment Conversion和Method Invocation Conversion:
-
一个拳击转换(§5.1.7),随后可以加宽引用转换
-
一个拆箱转换(第5.1.8节),可选地后跟一个加宽的基元转换。
加
- 扩大和缩小原始的转变
该规范确实说“转换转换比分配或方法调用转换更具包容性:转换可以执行除字符串转换或捕获转换之外的任何允许转换”
通过我的阅读,本条允许从int
到long
强制转换:
如果类型相同,或者通过加宽基元转换或缩小基元转换,可以通过标识转换将基本类型的值转换为另一基本类型。
将int
转换为long
是一种扩展的原始转换 。
这只是将Integer
从Integer
转换为int
,这是由最后一个项目符合的:
取消装箱转换
当然,在示例中甚至不需要long
演员表演。
考虑以下四个定义:
final Integer io = Integer.valueOf(45); final int i = io; final long l1 = (long)i; final long l2 = i;
你认为其中任何一个都令人惊讶吗? 你原来的例子看起来没什么不同; 它只是省略了中间变量。