Please consider 2 cases:
//1
Short s = 10; //obviously compiles
//2
takeShort(10); //error - int is not applicable
//where:
static void takeShort(Short s) {}
I assume that case 1 is changed by compiler to :
short _temp_s = 10;
Short s = Short.valueOf(_temp_s);
Could you please explain what compiler is trying to do in case 2, so it does not compile ? If it is not trying to apply autoboxing as it does in case 1, then why ?
EDIT
Reference to JSL in johnchen902 answer explains compiler's behaviour.
Still not exactly clear why JLS does not support "A narrowing primitive conversion followed by a boxing conversion" for Method Invocation Conversion as it does in Assignment Conversion for the case of constant expression of type byte, short, char, or int.
Any ideas ?
Short s = 10;
This is an Assignment Conversion
, and 10
is a constant expression. JLS said:
5.2. Assignment Conversion
Assignment conversion occurs when the value of an expression is assigned to a variable: the type of the expression must be converted to the type of the variable.
......
In addition, if the expression is a constant expression of type byte, short, char, or int:
- A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
- Short and the value of the constant expression is representable in the type short.
takeShort(10);
This is a Method Invocation Conversion
. JLS said:
5.3. Method Invocation Conversion
Method invocation conversion is applied to each argument value in a method or constructor invocation : the type of the argument expression must be converted to the type of the corresponding parameter.
Method invocation 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 widening reference conversion
- an unboxing conversion optionally followed by a widening primitive conversion.
......
If the type of the expression cannot be converted to the type of the parameter by a conversion permitted in a method invocation context, then a compile-time error occurs.
Unlike Assignment Conversion, non of conversion listed above can converts int
to Short
, so a compile-time error occurs.
Unfortunately some has rejected kiruwka's edit before I can approve it, so I edit it myself
The example of Method invocation conversion:
// takeInteger(int) takeDouble(double) takeObject(Object) takeIntegerObject(Integer)
takeInteger(5); // an identity conversion
takeDouble(5); // a widening primitive conversion
takeObject(new Integer(5)); // a widening reference conversion
takeIntegerObject(5); // a boxing conversion
takeObject(5); // a boxing conversion followed by widening reference conversion
takeInteger(new Integer(5)); // an unboxing conversion
takeDouble(new Integer(5)); // an unboxing conversion followed by a widening primitive conversion.
The literals like 10
are int
by default in Java. So, you are trying to assign int
to a method which takes Short
or short
as a parameter, which would require explicit casting. You can assign 10
to a short
variable, but you can't pass it as an argument to a method that accepts Short
or short
. You can cast it and pass it as follows:
takeShort((short)10);
EDIT :
The int
has a range of -2147483648
to 2147483647
, whereas short
has a range of -32768
to 32767
. Until and unless the value of the literal is within the range of short
, it would be Okay for the compiler to convert it to short
. But, as Boris the Spider mentioned in his comment, once the value of literal exceeds the range of short
, it won't be able to convert the literal to short
, since there would be data loss. Thus, compiler doesn't converts the literals to short
, when passsed as an argument in a method.