【问题标题】:Java casting: is the compiler wrong, or is the language spec wrong, or am I wrong?Java 铸造:是编译器错了,还是语言规范错了,还是我错了?
【发布时间】:2011-07-20 03:14:57
【问题描述】:

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

15.16 部分讨论强制转换表达式。它说如果参数类型无法通过强制转换转换为强制类型转换(第 5.5 节),则应该是编译时错误:

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

5.5 部分讨论了转换转换。它给出了允许的转换类型列表。列表中特别缺少的是“拆箱转换,然后是扩大/缩小原始转换”。 但是 javac 编译器(以及 Eclipse 编译器)似乎确实允许这种确切的转换顺序。例如:

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

... 编译得很好。 (有问题的转换是转换为long;参数是java.lang.Integer 类型,因此转换需要取消装箱到int,然后是扩大的原始转换)。

同样,根据 JLS,它应该不可能从 byte 转换为 char,因为(根据 5.1.4)需要扩大基元转换缩小基元转换 - 但是,编译器也允许这种转换。

谁能赐教?

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

【问题讨论】:

  • 一个更好的例子是long l = (long) Integer.valueOf(45);
  • 感谢您的建议,我已经编辑了问题。

标签: java javac jls


【解决方案1】:

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

这会编译:(Object)45 而这不会:(Long)45

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

  • 拳击转换 (§5.1.7) 可选地,然后加宽 参考转化

  • 拆箱转换 (§5.1.8) 可选地后跟一个扩大 原始转换。

  • 扩大和缩小原始对流

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

【讨论】:

  • (Object)45 只是一个拳击操作,对吧? (Long)45L 工作吗?我认为(Long)45 从技术上讲是两件事,拳击,然后投射到 Long,这是行不通的,因为 45 会被装箱为整数,而不是 Long。
  • (Object)45 是“装箱转换后扩大参考转换”,根据提议的规则是合法的。如果我们允许规则组合,(Long)45 可以工作,即“加宽原语”+“装箱”
  • 我认为你是对的。我注意到方法调用和赋值转换明确表示您只能使用列出的转换序列中的 一个,而强制转换并未明确说明只能使用一个:但是,允许例如,任意选择将允许不必要的原始缩小转换(随后是扩大) - 这可能会改变值 - 在身份转换就足够的情况下。
  • (当然,允许任意选择将允许您使用 (Long)45 的示例)。
  • JLS 不是最高质量的。很多地方都很乱。
【解决方案2】:

根据我的阅读,从intlong 的转换是由这个子句允许的:

如果类型相同,则可以通过恒等转换将原始类型的值转换为另一种原始类型,或者通过扩大原始类型转换或缩小原始类型转换。

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

剩下的就是从Integerint 的转换,这由最后一个项目符号表示:

拆箱转换

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

考虑以下四个定义:

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

您认为其中任何一个令人惊讶吗?您的原始示例看起来没有任何不同;它只是省略了中间变量。

【讨论】:

  • 您的意思是可以选择应用 5.5 中列出的所有转换,并且可以按任何顺序? (我的示例所需的顺序是首先进行拆箱转换,然后是扩大)。这使得列出身份转换变得多余,并且还不清楚为什么未经检查的转换是特殊情况(通过将其与扩大/缩小参考转换结合起来)。我相当确定只有 一个 列出的转化可以应用。
  • 虽然:我确实注意到强制转换并没有明确说明只能应用列出的转换序列之一,就像方法调用转换和赋值转换一样。
  • 其实5.5中的详细规则明确的说整个过程在某些情况下是递归应用的。这对我来说,这些规则肯定可以应用不止一次。
  • @Ted,你能给我确切的位置/文字吗?我能找到的最接近的是:“如果 T 是类型变量,则递归应用此算法,使用 T 的上限代替 T。” - 但这仅适用于从一种引用类型转换为另一种引用类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-22
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
相关资源
最近更新 更多