Java casting:是编译器错了,还是语言规范错了,还是我错了?

Java casting:是编译器错了,还是语言规范错了,还是我错了?,java,javac,jls,Java,Javac,Jls,我一直在阅读Java语言规范第3版,发现规范和javac编译器实现之间存在差异。Eclipse编译器中也存在同样的差异 第二节讨论演员的表达方式。它指出,如果参数类型无法通过强制转换转换为强制转换类型,则应为编译时错误(第5.5节): 如果根据强制转换规则(§5.5),操作数的编译时类型可能永远不会强制转换为强制转换运算符指定的类型,则为编译时错误。否则,在运行时,操作数值将通过强制转换转换(如有必要)转换为强制转换运算符指定的类型 第三节讨论铸造转换。它给出了允许的转换类型列表。列表中特别缺少

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

第二节讨论演员的表达方式。它指出,如果参数类型无法通过强制转换转换为强制转换类型,则应为编译时错误(第5.5节):

如果根据强制转换规则(§5.5),操作数的编译时类型可能永远不会强制转换为强制转换运算符指定的类型,则为编译时错误。否则,在运行时,操作数值将通过强制转换转换(如有必要)转换为强制转换运算符指定的类型

第三节讨论铸造转换。它给出了允许的转换类型列表。列表中特别缺少的是“先取消装箱转换,然后再扩大/缩小原语转换”然而javac编译器(以及Eclipse编译器)似乎确实允许这种精确的转换顺序。例如:

long l = (long) Integer.valueOf(45);
。。。编译得很好。(有问题的强制转换是对
long
的强制转换;参数的类型是
java.lang.Integer
,因此转换需要先取消装箱到
int
,然后再进行扩展原语转换)

同样,根据JLS,从
字节
转换为
字符
,应该是不可能的,因为(根据)需要扩大原语转换和缩小原语转换-但是,编译器也允许这种转换

谁能启发我


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

根据我的理解,本条款允许从
int
转换为
long

一个基元类型的值可以通过标识转换(如果类型相同)或通过扩大基元转换或缩小基元转换转换转换为另一个基元类型

int
转换为
long
是一种扩展原语转换

这只剩下从
整数
整数
的转换,最后一个项目符号提供了转换:

拆箱转换

当然,在本例中甚至不需要强制转换为
long

考虑以下四个定义:

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

你觉得他们有什么了不起吗?你最初的例子看起来没有什么不同;它只是省略了中间变量。

我认为你是对的,编译器是对的,规范是错的

它编译:
(Object)45
,而它不编译:
(Long)45

理解编译器行为(包括我正在使用的Intellij)的唯一方法是修改强制转换以符合赋值转换和方法调用转换:

  • 装箱转换(§5.1.7) (可选)后接加宽 参考转换

  • 拆箱转换(§5.1.8) (可选)后跟加宽区域 原语转换

加上

  • 扩大和缩小原始对流

该规范确实说过“强制转换比赋值或方法调用转换更具包容性:强制转换可以进行除字符串转换或捕获转换以外的任何允许的转换”

更好的例子是
long l=(long)Integer.valueOf(45)谢谢你的建议,我已经编辑了这个问题。你是说5.5中列出的所有转换都可以任意顺序应用?(我的示例所要求的顺序是先进行拆箱转换,然后进行加宽)。这使得列出标识转换是多余的,并且还不清楚为什么未检查的转换是特殊情况的(通过将其与加宽/缩小引用转换结合起来)。我相当肯定,只可以应用列出的转换中的一个。尽管:我注意到强制转换并没有像方法调用转换和赋值转换那样,明确地说只可以应用列出的转换序列中的一个。事实上,5.5中的详细规则明确指出,在某些情况下,整个过程是递归应用的。这就告诉我,这些规则肯定可以应用不止一次。@Ted,你能告诉我它的确切位置/文本吗?我能找到的最接近的是:“如果T是一个类型变量,那么这个算法是递归应用的,使用T的上限代替T。”——但这只适用于从一个引用类型转换到另一个引用类型。
(Object)45
只是一个装箱操作,对吗?
(长)45L
工作吗?我认为从技术上讲,
(Long)45
是两件事,装箱,然后转换为Long,这不起作用,因为45将装箱为整数,而不是Long。
(Object)45
是“装箱转换,然后扩大引用转换”,根据提议的规则是合法的<如果我们允许规则的组合,例如“加宽原语”+“装箱”,那么代码>(长)45
可能会起作用。我想你是对的。我注意到,方法调用和赋值转换明确表示只能使用列出的转换序列中的一个,而强制转换并没有明确说明只能使用一个:但是,允许任意选择将允许(例如)不必要的原语缩小转换(随后是加宽)-这可能会改变值-在身份转换已经足够的情况下。(另外,允许任意选择当然会允许您的示例
(Long)45
)。JLS不是最高质量的。在很多地方相当混乱。