10.3.2 自动装箱、拆箱与遍历循环
从纯技术的角度来讲,自动装箱、自动拆箱与遍历循环(Foreach循环)这些语法糖,无论是实现上还是思想上都不能和上文介绍的泛型相比,两者的难度和深度都有很大差距。专门拿出一节来讲解它们只有一个理由:毫无疑问,它们是Java语言里使用得最多的语法糖。我们通过代码清单10-6和代码清单10-7中所示的代码来看看这些语法糖在编译后会发生什么样的变化。
代码清单10-6 自动装箱、拆箱与遍历循环
public static void main(String[]args){
List<Integer>list=Arrays.asList(1,2,3,4);
//如果在JDK 1.7中,还有另外一颗语法糖[1]
//能让上面这句代码进一步简写成List<Integer>list=[1,2,3,4];
int sum=0;
for(int i:list){
sum+=i;
}
System.out.println(sum);
}
代码清单10-7 自动装箱、拆箱与遍历循环编译之后
public static void main(String[]args){
List list=Arrays.asList(new Integer[]{
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3),
Integer.valueOf(4)});
int sum=0;
for(Iterator localIterator=list.iterator();localIterator.hasNext();){
int i=((Integer)localIterator.next()).intValue();
sum+=i;
}
System.out.println(sum);
}
代码清单10-6中一共包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数5种语法糖,代码清单10-7则展示了它们在编译后的变化。泛型就不必说了,自动装箱、拆箱在编译之后被转化成了对应的包装和还原方法,如本例中的Integer.valueOf()与Integer.intValue()方法,而遍历循环则把代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口的原因。最后再看看变长参数,它在调用的时候变成了一个数组类型的参数,在变长参数出现之前,程序员就是使用数组来完成类似功能的。
这些语法糖虽然看起来很简单,但也不见得就没有任何值得我们注意的地方,代码清单10-8演示了自动装箱的一些错误用法。
代码清单10-8 自动装箱的陷阱
public static void main(String[]args){
Integer a=1;
Integer b=2;
Integer c=3;
Integer d=3;
Integer e=321;
Integer f=321;
Long g=3L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
}
阅读完代码清单10-8,读者不妨思考两个问题:一是这6句打印语句的输出是什么?二是这6句打印语句中,解除语法糖后参数会是什么样子?这两个问题的答案可以很容易试验出来,笔者就暂且略去答案,希望读者自己上机实践一下。无论读者的回答是否正确,鉴于包装类的“==”运算在不遇到算术运算的情况下不会自动拆箱,以及它们equals()方法不处理数据转型的关系,笔者建议在实际编码中尽量避免这样使用自动装箱与拆箱。
[1]在本章完稿之后,此语法糖随着Project Coin一起被划分到JDK 1.8中了,在JDK 1.7里不会包括。