转自:http://www.diybl.com/course/3_program/java/javaxl/20071220/92739.html
1. Java的泛型实现采用"擦除法".
编译器为我们完成类型擦除和必要的类型转换, 在运行时,每个泛型类只有一种类型. 具体地说, List<Integer>, List<String> 和 List<List<String>> 在运行时都将具有相同的类型: List
//泛型是1.5中引入的一个新的概念,由于不用进行强制转换类型了,所以具有较高的安全性和易用性。因为泛型其实只是在编译器中实现的而虚拟机并不认识泛型类项,所以要在虚拟机中将泛型类型进行擦除。也就是说,在编译阶段使用泛型,运行阶段取消泛型,即擦除。 擦除是将泛型类型以其父类代替,如String 变成了Object等。其实在使用的时候还是进行带强制类型的转化,只不过这是比较安全的转换,因为在编译阶段已经确保了数据的一致性。
2. Boxing and Unboxing 自动装箱 和 自动拆箱
特别注意在 == 比较时, 自动装箱和自动拆箱所造成的影响, 如下面代码:
public static int sum(List<Integer> ints) {
int s = 0;
for (int n : ints) { s += n; }
return s;
}
public static Integer sumInteger(List<Integer> ints) {
Integer s = 0;
for (Integer n : ints) { s += n; }
return s;
}
上面两个方法实现相同的功能, 前者采用Java 5的新特性, 相比下效率会比后者快大概一倍.??
List<Integer> bigs = Arrays.asList(100,200,300);
assert sumInteger(bigs) == sum(bigs); // 应用自动拆箱, 比较两个int, 结果为true
assert sumInteger(bigs) != sumInteger(bigs); // 比较Integer对象, 结果为false
上面代码还比较好理解, Java在实现装箱时采用了缓存机制, 缓存-128 ~ 127之间的整数, ''\u0000'' ~ ''\u007f''之间的char类型, byte类型, 以及boolean可以提高效率. 对其它范围的数据, Java标准也允许对其进行缓存, 所以上面的第二个断言有可能会失败.
List<Integer> smalls = Arrays.asList(1,2,3);
assert sumInteger(smalls) == sum(smalls); // 比较int, 结果肯定为true
assert sumInteger(smalls) == sumInteger(smalls); // 由于缓存, 两个Integer其实是同一个对象, 所以结果也为true
结论: 不要依赖于自动装箱和自动拆箱的缓存机制, 对于对象的比较, 任何时候都应该使用equals方法而不是==.
3. 泛型方法和可变参数方法(Varargs)
class Lists {
public static <T> List<T> toList(T... arr) {
List<T> list = new ArrayList<T>();
for (T elt : arr) list.add(elt);
return list;
}
}
上面是使用泛型和可变参数的一个简单例子, 调用该方法时, 我们可以传入连续的几个参数, 也可以传入一个参数的数组. 其实可变参数方法唯一的好处就是方便, 它让我们不用自己把实参打包到一个数组中. Java编译器为我们完成这一工作, 实际上, 在运行时, 上面方法其实就是使用参数的数组来实现.
调用泛型方法时, 如果参数类型不明确, 我们就需要明确地指出类型信息, 否则就会引起"未检查的警告消息", 使用如下的格式:
List<Integer> ints = Lists.<Integer>toList();
List<Object> objs = Lists.<Object>toList(1, "two");
另外, Java的语法要求: 在使用明确的类型调用方法时, 必需使用带有 "." 的格式. 以上面Lists类为例, 即使是在Lists类内部的方法中, 也不可以使用 List<Integer> ints = <Integer>toList(); 而必需使用 List<Integer> ints = Lists.<Integer>toList();