Java强制转换:是编译器错误,还是语言规范错误,还是我错了?

发布于 2024-10-24 12:32:56 字数 1276 浏览 5 评论 0原文

我一直在阅读 Java 语言规范,第三版,并发现我认为规范与 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) 需要扩大原始转换缩小原始转换 - 然而,编译器也允许这种转换。

谁能启发我吗?

编辑:自从提出这个问题以来,我已经提交了错误报告< /a> 与 Oracle。他们的回应是这是“JLS 中的一个故障”。

I have been reading the Java Language Spec, 3rd edition, and have found what I think is a discrepancy between the spec and the javac compiler implementation. The same discrepancies exist in the Eclipse compiler.

Section 15.16 talks about cast expressions. It says that it should be a compile time error if the argument type cannot be converted to the cast type via casting conversion (section 5.5):

It is a compile-time error if the compile-time type of the operand may never be cast to the type specified by the cast operator according to the rules of casting conversion (§5.5). Otherwise, at run-time, the operand value is converted (if necessary) by casting conversion to the type specified by the cast operator.

Section 5.5 talks about casting conversion. It gives a list of conversion types which are allowed. Specifically absent from the list is "unboxing conversion followed by widening/narrowing primitive conversion". However that exact sequence of conversions does seem to be allowed by the javac compiler (and also the Eclipse compiler). For instance:

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

... compiles just fine. (The problematic cast is the cast to long; the argument is of type java.lang.Integer, so the conversion requires unboxing to int followed by a widening primitive conversion).

Likewise, according to the JLS it should not be possible to cast from byte to char, because that (according to 5.1.4) requires a widening primitive conversion and a narrowing primitive conversion - however, this cast is also allowed by the compilers.

Can anyone enlighten me?

Edit: since asking this, I have filed a bug report with Oracle. Their response is that this is a "glitch in the JLS".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

隱形的亼 2024-10-31 12:32:56

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

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

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

  • 装箱转换(第 5.1.7 节)
    可选地随后加宽
    参考转换

  • 拆箱转换(§5.1.8)
    可选地随后加宽
    原始转换。

加宽

  • 和缩小原始转换规范

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

I think you are right, the compilers are right, and the spec is wrong....

This compiles: (Object)45 and this does not: (Long)45

The only way to make sense of the compilers' behavior (including Intellij I'm using) is if Casting Conversion is modified to agree with Assignment Conversion and Method Invocation Conversion:

  • a boxing conversion (§5.1.7)
    optionally followed by widening
    reference conversion

  • an unboxing conversion (§5.1.8)
    optionally followed by a widening
    primitive conversion.

plus

  • widening and narrowing primitive convesion

The spec did say "casting conversions are more inclusive than assignment or method invocation conversions: a cast can do any permitted conversion other than a string conversion or a capture conversion"

蹲在坟头点根烟 2024-10-31 12:32:56

根据我的阅读,此子句允许从 intlong 的转换:

如果类型相同,可以通过身份转换将基本类型的值转换为另一个基本类型,或者通过扩大基本类型转换或缩小基本类型转换。

int 转换为 long 是一种加宽基元转换

只剩下从 Integerint 的转换,这由最后一个项目符号容纳:

拆箱转换

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

考虑以下四个定义:

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

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

By my reading, the cast from int to long is permitted by this clause:

A value of a primitive type can be cast to another primitive type by identity conversion, if the types are the same, or by a widening primitive conversion or a narrowing primitive conversion.

Converting int to long is a widening primitive conversion.

That just leaves the conversion from Integer to int, which is accommodated by the last bullet:

an unboxing conversion

Of course, the cast to long isn't even necessary in the example.

Consider the following four definitions:

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

Do you consider any of them surprising? Your original example doesn't look any different; it merely elides the intermediate variables.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文