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,不应该从bytechar ,因为(根据5.1.4 )需要扩展原始转换缩小基元转换 – 但是,编译器也允许这种转换。

任何人都可以开导我吗?

编辑:自从问这个以来,我已经向Oracle提交了一份错误报告 。 他们的反应是,这是“JLS中的一个小故障”。

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

这编译:( (Object)45 ,这不:( (Long)45

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

  • 一个拳击转换(§5.1.7),随后可以加宽引用转换

  • 一个拆箱转换(第5.1.8节),可选地后跟一个加宽的基元转换。

  • 扩大和缩小原始的转变

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

通过我的阅读,本条允许从intlong强制转换:

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

int转换为long是一种扩展的原始转换

这只是将IntegerInteger转换为int ,这是由最后一个项目符合的:

取消装箱转换

当然,在示例中甚至不需要long演员表演。

考虑以下四个定义:

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

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