[简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)
两种限定通配符
- 表示类型的上界,格式为:<? extends T>,即类型必须为T类型或者T子类
- 表示类型的下界,格式为:<? super T>,即类型必须为T类型或者T的父类
PECS原则
生产者只能用extends,消费者只能用super
上代码
public class Fruit {
}
public class Apple extends Fruit{
}
public class Banana extends Fruit{
}
List<? extends Fruit> list 的理解
正如表面意思<? extends Fruit>表示的是泛型的类型是Fruit或者Fruit的子类,也就是说我们给list赋值时泛型可以写成Fruit或者Fruit的子类,可以是 new ArrayList(),也可以是new ArrayList(),还可以是new ArrayList()
看有些地方说"<? extends Fruit>表示list中的元素可能是Fruit或者Fruit的子类",感觉这种说法很有问题,
private static List<? extends Fruit> getExtendsList() {
List<? extends Fruit> list;
// Explicit type argument Fruit can be replaced with <> 意思就是new ArrayList<Fruit>() 可以写成 new ArrayList<>()
list = new ArrayList<Fruit>();
list = new ArrayList<Apple>();
list = new ArrayList<Banana>();
return list;
}
list的具体定义是new ArrayList(), new ArrayList(),还是new ArrayList(),在m1中是不知道的,最大的泛型是new ArrayList(),所以取出来的一定是Fruit,最小泛型new ArrayList(),new ArrayList()…可能有很多个,具体是哪个没办法确定,所以也没办法往里面添加元素
private static void m1( List<? extends Fruit> list) {
Apple apple = new Apple();
// 有可能list的定义是 list = new ArrayList<Banana>(),所以往里面添加Apple就会有问题
// list.add(apple);
}
List<? super Fruit> list的理解
正如表面意思,泛型的类型是Fruit或者Fruit的父类,也就是我们给list赋值时泛型可以写Fruit或者Fruit的父类,可以是new ArrayList()也可以是 new ArrayList();
如果理解成"list中的元素是Fruit或Fruit的父类,仔细品品,漏洞百出,试想如果Fruit是个顶级接口呢?那岂不是list中只能放Object了?而且还只能是Object类本身?"
private static List<? super Fruit> getSuperList() {
List<? super Fruit> list;
list = new ArrayList<Fruit>();
list = new ArrayList<Object>();
return list;
}
list的具体定义到底是new ArrayList()或者new ArrayList()在m2方法中是没法确定的,所以往list中添加元素只能按照最小泛型处理,即按照new ArrayList()处理,获取元素时按照最大的泛型处理,即new ArrayList(),所以拿到的元素都是Object(都Object了,泛型也就没意义了),所以<? super Fruit>的意义在于往集合中添加元素,也就是"消费者只能用super"
private static void m2( List<? super Fruit> list) {
Apple apple = new Apple();
list.add(apple);
Banana banana = new Banana();
list.add(banana);
}
总结
- List<? extends Fruit> list 限定通配符泛型(还不确定的泛型,但是有范围),一般用于只获取
- List<? super Fruit> list 限定通配符泛型(还不确定的泛型,但是有范围), 一般用于只添加(也可以获取,但是获取出来的是Object,没啥意义)
- List list 明确的泛型,可获取,也可添加,也是我们用的最多的泛型
JDK中的PECS
java.util.Collections#copy
public static <T> 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<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}