Is this really widening vs autoboxing?

☆樱花仙子☆ 提交于 2019-12-08 14:28:22

问题


I saw this in an answer to another question, in reference to shortcomings of the Java spec:

There are more shortcomings and this is a subtle topic. Check this out:

public class methodOverloading{
     public static void hello(Integer x){
          System.out.println("Integer");
     }

     public static void hello(long x){
          System.out.println("long");
     }

     public static void main(String[] args){
         int i = 5;
         hello(i);
     }
}

Here "long" would be printed (haven't checked it myself), because the compiler chooses widening over auto-boxing. Be careful when using auto-boxing or don't use it at all!

Are we sure that this is actually an example of widening instead of autoboxing, or is it something else entirely?

On my initial scanning, I would agree with the statement that the output would be "long" on the basis of i being declared as a primitive and not an object. However, if you changed

hello(long x)

to

hello(Long x)

the output would print "Integer"

What's really going on here? I know nothing about the compilers/bytecode interpreters for java...


回答1:


In the first case, you have a widening conversion happening. This can be see when runinng the "javap" utility program (included w/ the JDK), on the compiled class:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   i2l
   4:   invokestatic    #6; //Method hello:(J)V
   7:   return

}

Clearly, you see the I2L, which is the mnemonic for the widening Integer-To-Long bytecode instruction. See reference here.

And in the other case, replacing the "long x" with the object "Long x" signature, you'll have this code in the main method:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   invokestatic    #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   6:   invokestatic    #7; //Method hello:(Ljava/lang/Integer;)V
   9:   return

}

So you see the compiler has created the instruction Integer.valueOf(int), to box the primitive inside the wrapper.




回答2:


Yes it is, try it out in a test. You will see "long" printed. It is widening because Java will choose to widen the int into a long before it chooses to autobox it to an Integer, so the hello(long) method is chosen to be called.

Edit: the original post being referenced.

Further Edit: The reason the second option would print Integer is because there is no "widening" into a larger primitive as an option, so it MUST box it up, thus Integer is the only option. Furthermore, java will only autobox to the original type, so it would give a compiler error if you leave the hello(Long) and removed hello(Integer).




回答3:


Another interesting thing with this example is the method overloading. The combination of type widening and method overloading only working because the compiler has to make a decision of which method to choose. Consider the following example:

public static void hello(Collection x){
   System.out.println("Collection");
}

public static void hello(List x){
   System.out.println("List");
}

public static void main(String[] args){
   Collection col = new ArrayList();
   hello(col);
}

It doesn't use the run-time type which is List, it uses the compile-time type which is Collection and thus prints "Collection".

I encourage your to read Effective Java, which opened my eyes to some corner cases of the JLS.



来源:https://stackoverflow.com/questions/4922/is-this-really-widening-vs-autoboxing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!