[简单粗暴]一文彻底搞懂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());}}
}