I am mystified by the behavior of the Java compiler when assigning primitives to wrapper class references. Please see the code below. The lines with comments don\'t compile.
This seems to be compiler-specific behavior. When I paste your code into Eclipse, running Java 7, I do not see the compiler errors you report for short
to Integer
or byte
to Integer
.
Instead, I see byte
, short
, and int
can all be assigned to Byte
, Short
, and Integer
, but not Long
, and long
can only be assigned to Long
. Interestingly, if you change the variables to primitives instead of wrapper types, the byte
, short
, and int
behavior doesn't change, but now the assignments from the other types to long
also work.
javac 1.7.0_02
| byte | Byte || short | Short || int | Integer || long | Long |
From byte | Yes | Yes || Yes | Yes || Yes | No || Yes | No |
From short | Yes | Yes || Yes | Yes || Yes | No || Yes | No |
From int | Yes | Yes || Yes | Yes || Yes | Yes || Yes | No |
From long | No | No || No | No || No | No || Yes | Yes |
Eclipse Indigo
| byte | Byte || short | Short || int | Integer || long | Long |
From byte | Yes | Yes || Yes | Yes || Yes | Yes || Yes | No |
From short | Yes | Yes || Yes | Yes || Yes | Yes || Yes | No |
From int | Yes | Yes || Yes | Yes || Yes | Yes || Yes | No |
From long | No | No || No | No || No | No || Yes | Yes |
Given that different compilers allow different conversions, I suspect the "correct" behavior is not actually spelled out in the JLS. It seems certain conversions are done under the covers because the compiler writers considered it convenient (e.g. byte a = (int)1
is allowed but byte a = (int)1000
is not), not because it's a documented part of the language.
Let's look at the types of conversions allowed in an assignment context.
Principally:
Assignment contexts allow the use of one of the following:
an identity conversion
a widening primitive conversion
a widening reference conversion
a boxing conversion optionally followed by a widening reference conversion
an unboxing conversion optionally followed by a widening primitive conversion.
(Note my emphasis on one.)
Most of your examples that do not compile, for example
Integer s11 = (short)7;
require a widening primitive conversion followed by a boxing conversion. This is not a permitted conversion.
But then you might wonder why the following example does compile:
Byte s9 = (short)7;
This is a narrowing primitive conversion followed by a boxing conversion.
This is a special case:
In addition, if the expression is a constant expression of type
byte
,short
,char
, orint
[...] a narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte
and the value of the constant expression is representable in the typebyte
.
Short
and the value of the constant expression is representable in the typeshort
.
Character
and the value of the constant expression is representable in the typechar
.
This special case is necessary because there is no way to express an integer literal of a type narrower than int
.
From my research, I found that a byte is an 8-bit signed integer. Shorts are 16-bit signed integers. Therefore I can see why they are compatible, they are both two's complement signed integers, emphasis on signed. A long is a 64-bit integer, but it can also be unsigned (considering it has methods for comparing unsigned longs). This would probably explain why your conversions to long are causing errors - you'd be casting a signed byte to a potentially unsigned long. (Source: Reading about primitives at http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html)