Java casting: is the compiler wrong, or is the language spec wrong, or am I wrong?
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 con开发者_JS百科version (§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".
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"
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.
精彩评论