Java6与 Java7之间自动拆箱的差异

我已经注意到 JavaSE6和 JavaSE7之间的自动取消装箱行为的不同。我想知道为什么会这样,因为我找不到任何关于这两个版本之间的行为变化的文档。

这里有一个简单的例子:

Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

这可以用 JavaSE7中的 javac 进行良好的编译。但是,如果我给编译器“-source 1.6”参数,我会在最后一行得到一个错误:

inconvertible types
found   : java.lang.Object
required: int

我尝试下载 JavaSE6,使用本地版本6编译器进行编译(没有任何源代码选项)。它同意并给出与上面相同的错误。

怎么回事?从更多的实验来看,Java6中的解压缩似乎只能解压缩明显(在编译时)属于已装箱类型的值。例如,这两个版本都适用:

Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

因此,在 Java6和 Java7之间,取消装箱功能似乎得到了增强,因此它可以一次性强制转换和取消装箱对象类型,而不需要(在编译时)知道值是正确的装箱类型。然而,通过阅读 Java 语言规范或者在 Java 7出现的时候写的博客文章,我看不到任何变化,所以我想知道这个变化是什么,这个“特性”叫什么?

只是一个好奇心: 由于变化,有可能触发“错误的”拆箱:

Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];

这可以很好地编译,但是在运行时会出现 ClassCastException 异常。

有什么线索吗?

8451 次浏览

You are right; to put it more simply:

Object o = new Integer(1234);
int x = (int) o;

This works in Java 7, but gives a compilation error in Java 6 and below. Strangely, this feature is not prominently documented; for example, it's not mentioned here. It's debatable if it's a new feature or a bug fix (or a new bug?), see some related info and discussion. The consensus seems to point to an ambiguity in the original spec, which led to a slightly incorrect/inconsistent implementation on Java 5/6, which was fixed in 7, because it was critical for implementation of JSR 292 (Dynamically Typed Languages).

Java autoboxing has now some more traps and surprises. For example

Object obj = new Integer(1234);
long x = (long)obj;

will compile, but fail (with ClassCastException) at runtime. This, instead, will work:

long x = (long)(int)obj;

It looks like the language in section 5.5 Casting Conversion of Java 7 JLS was updated in comparison to the same section in the Java 5/6 JLS, probably to clarify the allowed conversions.

Java 7 JLS says

An expression of a reference type may undergo casting conversion to a primitive type without error, by unboxing conversion.

Java 5/6:

A value of a reference type can be cast to a primitive type by unboxing conversion (§5.1.8).

The Java 7 JLS also contains a table (table 5.1) of allowed conversions (this table is not included in the Java 5/6 JLS) from reference types to primitives. This explicitly lists casts from Object to primitives as a narrowing reference conversion with unboxing.

The reason is explained in this email:

Bottom line: If the spec. allows (Object)(int) it must also be allowing (int)(Object).