为什么需要泛型?
List list = new ArrayList();
list.add("A");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); //取出Integer时,运行时出现异常
System.out.println("name:" + name);
}
使用泛型
List<String> list = new ArrayList<String>();
通过List ,直接限定了list集合中只能含有String类型的元素,集合记住了元素的类型,无需进行强制转换
Heap Pollution
什么是堆污染:
堆污染是一种技术用语,它指的是对象的引用类型不是指向对象类型或者超类类型。
List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // 指向as列表,造成堆污染
// 这会导致ClassCastExceptions
所以,当将一个不带泛型的对象赋值给带泛型的变量是,通常会引发堆污染。
什么时候会发生堆污染
把一个不带泛型的对象赋给一个带泛型的变量是,就会发生堆污染. 通常编译期间都会检测出这种情况,并且显示警告: unchecked warning
.
List ns = new ArrayList<Number>();
List<String> ls = ns;
为什么会发生堆污染
在Java中,参数化类型(泛型),都是非具体化类型(non-reifiable types)。
non-reifiable types 是指,在runtime时期是不完整的类型,在编译期间,非具体化类型会经过 “类型擦除”(编译器会删除域类型参数相关的信息),所以准确的说,泛型只存在于编译时期。
非具体化类型保证了那些使用JDK1.5(没有泛型)之前的程序的二进制兼容性。
因此,不同泛型的同一类型变量,在运行时具有相同的类或接口实现。所以,当参数化类型的变量(List<String>
)指的不是该参数化类型的对象(List<Integer>
)时,就发生了堆污染。
Set s = new TreeSet<Integer>();
Set<String> ss = s; // unchecked warning
s.add(new Integer(42)); // another unchecked warning
Iterator<String> iter = ss.iterator();
while (iter.hasNext())
{
String str = iter.next(); // ClassCastException thrown
System.out.println(str);
}
parameterized vararg type 为什么会发生堆污染警告
有如下参数化可变参数类型的方法会发生堆污染警告:
public static <T> void foo(List<T>... bar) {
for (List<T> ts : bar) {
System.out.println(ts);
}
}
// warning: Possible heap pollution from parameterized vararg type
参数化可变参数类型可能发生堆污染,这是因为,可变参数在编译时期,会做出如下转换:
public static <T> void foo(List<T>... bar)
函数,被转换为public static <T> void foo(List<T>[] bar)
,然后再转换为public static <T> void foo(List[] bar)
这样,我们如下调用:
Object[] objectArray = new String[]{};
List<Integer>[] lists = (List<Integer>[]) objectArray;
foo(lists);
就会发生ClassCastException
异常,(java.lang.String; cannot be cast tojava.util.List)。所以,会出现堆污染警告。