autoboxing of numeric literals : wrapper initialization vs passing method arguments inconsistency
Asked Answered
C

2

7

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 ?

Cinerarium answered 12/5, 2013 at 9:29 Comment(0)
F
3
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.
Fronde answered 12/5, 2013 at 10:3 Comment(3)
Perfect, man! JLS Method Invocation Conversion explains it all. I would like to give simple examples of MIC cases explained above appended to your answer.Cinerarium
I think you should revise your unboxing conversion examples: method should be having primitive type as declared parameter, i.e. takeint and takedouble instead of takeInteger and takeDoubleCinerarium
@Cinerarium But that broke (my) naming conventions : See hereFronde
N
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.

Nelidanelie answered 12/5, 2013 at 9:33 Comment(4)
yes, I understand that the method requires short(or Short) to compile. My questions is : why compiler could not do conversion by itself, like, for example it does when compiling: Short S2 = (int) 10; short s2 = (int) 10;Cinerarium
@Cinerarium because then what happens with I do takeShort(Short.MAX_VALUE + 1)?Clowers
@Boris the Spider I expect that compiler could do exactly same range check when initializing Short(or short) with compile-time constant, such as final int int_const = 10; Short S2 = int_const;Cinerarium
@Cinerarium no because inputs to methods are not generally known at compile time. If I call takeShort(x) the compiler has no idea where x came from or its value.Clowers

© 2022 - 2024 — McMap. All rights reserved.