语法糖可以看做是编译器实现的一些“小把戏”,这些“小把戏”可能会使得效率“大提升”。
1. 泛型与类型擦除
本质是参数化类型的应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类,接口和方法的创建中,分别称为泛型类,泛型接口和泛型方法。泛型其实是 javac 提供给我们的一颗语法糖,因为它在编译阶段采用类型擦除,将泛型还原为原生类型(裸类型),并且在相应的地方插入了强制转型代码。例如:ArrayList list在编译后,我们再反编译class文件,可以看到代码变成了ArrayList list。
public class GenericTypes {
public static void method(List<String> list) {
System.out.println("invoke method(List<String> list)");
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
}
}
上述代码不能够被编译,因为参数List和List编译之后都被擦除了,变成了一样的原生类型List,擦除动作导致这两种方法的特征签名变得一摸一样。
public class GenericTypes {
public static String method(List<String> list) {
System.out.println("invoke method(List<String> list)");
return "";
}
public static int method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
return 1;
}
public static void main(String[] args) {
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
}
/**
输出:
invoke method(List<String> list
invoke method(List<Integer> list)
**/
上述代码能够运行的原因的是存在不同的返回值。
2. 自动装箱、拆箱与遍历循环
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
// 如果在JDK 1.7中,还有另外一颗语法糖 ,
// 能让上面这句代码进一步简写成List<Integer> list = [1, 2, 3, 4];
int sum = 0;
for (int i : list) {
sum += i;
}
System.out.println(sum);
}
上述代码编译之后:
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);
}
自动装箱的陷阱:
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));
}
/**
输出:
true
false
true
true
true
false
**/
原因:
// 反编译后的样子
public static void main(String[] args) throws InterruptedException {
Integer a = Integer.valueOf(1);
Integer b = Integer.valueOf(2);
Integer c = Integer.valueOf(3);
Integer d = Integer.valueOf(3);
Integer e = Integer.valueOf(321);
Integer f = Integer.valueOf(321);
Long g = Long.valueOf(3L);
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c.intValue() == a.intValue() + b.intValue());
System.out.println(c.equals(Integer.valueOf(a.intValue() + b.intValue())));
System.out.println(g.longValue() == (long)(a.intValue() + b.intValue()));
System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));
}
// Integer 源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Integer 在默认情况下对 -128 到 127 之间的数做了缓存,所以 c 和 d 的 valueof 都返回同一个对象。而 e 和 f 不在缓存范围内,所以分别单独为一个对象,而 == 运算在不遇到算术运算的情况下不会自动拆箱,所以比较的不是同一个对象。g == (a + b)
中有 + 运算,则会拆箱判断值;equals方法不会处理类型转换,类型相同则比较值,类型不同则直接不等。