关于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被声明为原始而不是对象,输出将为"长"。但是,如果您更改了
到
输出将显示" 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的某些极端情况大开眼界。