关于java:这真的扩展了vs自动装箱吗?

关于java:这真的扩展了vs自动装箱吗?

Is this really widening vs autoboxing?

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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被声明为原始而不是对象,输出将为"长"。但是,如果您更改了

1
hello(long x)

1
hello(long x)

输出将显示" Integer"

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


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

1
2
3
4
5
6
7
8
9
10
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",您将在主要方法中使用以下代码:

1
2
3
4
5
6
7
8
9
10
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),以将原始容器装箱在包装器中。


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

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
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的某些极端情况大开眼界。


推荐阅读