这真的比自动装箱要宽吗?

时间:2020-03-05 18:38:22  来源:igfitidea点击:

我在参考Java规范的缺点回答另一个问题时看到了这一点:

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!

我们确定这实际上是扩展而不是自动装箱的示例,还是完全是其他东西?

在我的初始扫描中,我同意这样的说法,即基于将" i"声明为原始而不是对象,输出将为" long"。但是,如果我们更改了

hello(long x)

hello(Long x)

输出将显示" Integer"

这到底是怎么回事?我对Java的编译器/字节码解释器一无所知...

解决方案

回答

是的,在测试中尝试一下。我们将看到" long"字样。之所以要扩展,是因为Java在选择将int自动装箱为Integer之前会选择将int扩展为很长的时间,因此选择了调用hello(long)方法。

编辑:被引用的原始帖子。

进一步的编辑:第二个选项将打印Integer的原因是因为没有"扩大"到更大的原语作为选项,因此必须将其装箱,因此Integer是唯一的选项。此外,java只会自动装箱到原始类型,因此,如果我们离开hello(Long)并删除了hello(Integer),它将给出编译器错误。

回答

在第一种情况下,我们正在发生扩大的转换。在已编译的类上运行" javap"实用程序(包含在JDK中)时,可以看到以下内容:

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

}

显然,我们会看到I2L,它是扩展的Integer-To-Long字节码指令的助记符。请参阅此处的参考。

在另一种情况下,将" long x"替换为对象" Long x"签名,我们将在main方法中获得以下代码:

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

}

因此,我们会看到编译器创建了指令Integer.valueOf(int),以将原语放入包装器中。

回答

此示例的另一件有趣的事是方法重载。类型扩展和方法重载的结合只能起作用,因为编译器必须决定选择哪种方法。考虑以下示例:

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);
}

它不使用List的运行时类型,而是使用Collection的编译时类型,因此打印" Collection"。

我鼓励我们阅读"有效的Java",这使我对JLS的一些极端案例大开眼界。