// 原生态可以指向任意List
List list = new ArrayList()
list.add(0) // 可正常添加。并不受ArrayList() 的String影响
// 误以为list都是String,强转String的时候就会崩
String s = (String)list.get(0)
// List
任意存在继承关系的两个类,如Integer 是 Number的子类。
泛型是不变的。
则其对应的泛型不存在继承关系,如List 不是 List 的子类
所以下面的:
ArrayList number = new ArrayList() // error!
ArrayList number = new ArrayList() // error!
泛型的不变,从直觉上看,这很奇怪。但是这很有意义。毕竟List可以放进Double的数据,但是List只能放进Integer的数据。如果ArrayList number = new ArrayList()成立,也就是number.add(1d)也成立。那么读取数据进行操作的时候很就得崩。
反观数组,下面代码编译时通过的,但是执行的时候,就得boom boom boom
Number[] number = new Integer[10];
number[0] = 1d; // ArrayStoreExecption;
数组是协变,所以泛型是不变的。泛型把类型安全的检测提前到了编译期,而不是等到运行时,才去发现问题。
3、有限制的通配符
泛型是不变的。但是为了api的灵活性,JDK提供了使泛型支持协变和逆变的方法。
1. extend ---使得泛型支持协变
List b = new ArrayList<>();
List n= new ArrayList;
n.addAll(b);
上述代码是可以正常执行的,Number类型的添加一下Integer数据,正常不过的事情。
但是addAll(..)的参数该如何定义呢?通用点就应该是
public interface List extends Collection {
addAll(Collection c)
}
Listb = new ArrayList<>();
b.add(1d)
List super Integer> n= b; // 甚至可以是 n = new ArrayList()
n.add(0)
那么显然我们处理 n.get(0) 无法判断其具体类型,只能退化到Object.
4、稍总结下
总的来说,extend适合作为生产者。比如addAll(Collection extend Number> c) 限制所有c中所有数据都得起码是Number。适合作为一个生产者来提供数据。
而super适合作为消费者,Collection super Number> c 则限制数据的流入,想要被c消费使用(c.add(Number))的数据起码为Number。
看这个例子:
//生产者:src,数据类型起码为T;传入消费者dest中,dest要求传入的数据类型起码为T
public static void copy(List super T> dest, List extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i di=dest.listIterator();
ListIterator extends T> si=src.listIterator();
for (int i=0; i