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里不会包括。