autoboxing of numeric literals : wrapper initializ

2019-01-26 09:42发布

问题:

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 ?

回答1:

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.


回答2:

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.