关于Java的泛型,我们经常使用<? super T>和<? extends T> 两种方式进行变量声明,这里面涉及一个叫PECS的概念,如果理解不到位 可能会造成对集合进行set或get的时候编译报错,让你一头雾水。
概念
PECS的意思是Producer Extends Consumer Super
,简单理解为如果是生产者则使用Extends,如果是消费者则使用Super,不过,这到底是啥意思呢?
下面的解释来自StackOverflow
,很精辟:
tl;dr: “PECS” is from the collection’s point of view. If you are only pulling items from a generic collection, it is a producer and you should use extends; if you are only stuffing items in, it is a consumer and you should use super. If you do both with the same collection, you shouldn’t use either extends or super.
让我们通过一个典型的例子理解一下到底什么是Producer和Consumer:
1
2
3
4
5
6
7
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i=0; i<src.size(); i++) {
dest.set(i, src.get(i));
}
}
}
上面的例子中将src中的数据复制到dest中,这里src就是生产者,它「生产」数据,dest是消费者,它「消费」数据。设计这样的方法,好处就是,可以复制任意类型的List,通用性特别强, 不用使用Object来进行强制转换。
1
2
3
Object做为参数完全可以替代泛型,但是使用Object做为参数,就可以用任何类型,不能限定类型。
使用泛型,可以帮助开发人员在编译的时候就能定位错误。
而使用Object,只能在运行的时候才能发现错误。
举个例子
举一个水果相关的例子:
-
在
List<? extends Fruit>
的泛型集合中,对于元素的类型,编译器只能知道元素是继承自Fruit,具体是Fruit的哪个子类是无法知道的。 所以「向一个无法知道具体类型的泛型集合中插入元素是不能通过编译的」。但是由于知道元素是继承自Fruit,所以从这个泛型集合中取Fruit类型的元素是可以的。 -
在
List<? super Apple>
的泛型集合中,元素的类型是Apple的父类,但无法知道是哪个具体的父类,因此「读取元素时无法确定以哪个父类进行读取」。 插入元素时可以插入Apple与Apple的子类,因为这个集合中的元素都是Apple的父类,子类型是可以赋值给父类型的。
来看一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Fruit {}
public class Apple extends Fruit {}
public class AppleLite extends Apple {}
public void testCoVariance(List<? extends Apple> apples) {
Apple b = new Apple();
AppleLite c = new AppleLite();
apples.add(b); // does not compile
apples.add(c); // does not compile
Fruit a = myBlist.get(0);
}
public void testContraVariance(List<? super Apple> apples) {
Apple b = new Apple();
AppleLite c = new AppleLite();
apples.add(b);
apples.add(c);
Fruit a = apples.get(0); // does not compile
}
发现上面凡是? extends T
的集合,也就是Producer都不能「写」,凡是? super T
的集合,也就是Consumer都不能「读」。
有一个比较好记的口诀:
- 只读不可写时,使用List<? extends Fruit>:Producer
- 只写不可读时,使用List<? super Apple>:Consumer
A producer is allowed to produce something more specific, hence extends, a consumer is allowed to accept something more general, hence super.
References
本文首次发布于 LiuShuo’s Blog, 转载请保留原文链接.