什麼是PECS? java
PECS指「Producer Extends,Consumer Super」。換句話說,若是參數化類型表示一個生產者,就使用<? extends T>;若是它表示一個消費者,就使用<? super T>,可能你還不明白,不過不要緊,接着往下看好了。spa
下面是一個簡單的Stack的API接口:code
1
2
3
4
5
6
|
public
class
Stack<E>{
public
Stack();
public
void
push(E e):
public
E pop();
public
boolean
isEmpty();
}
|
假設想增長一個方法,按順序將一系列元素所有放入Stack中,你可能想到的實現方式以下:對象
1
2
3
4
|
public
void
pushAll(Iterable<E> src){
for
(E e : src)
push(e)
}
|
假設有個Stack<Number>,想要靈活的處理Integer,Long等Number的子類型的集合接口
1
2
3
|
Stack<Number> numberStack =
new
Stack<Number>();
Iterable<Integer> integers = ....;
numberStack.pushAll(integers);
|
此時代碼編譯沒法經過,由於對於類型Number和Integer來講,雖而後者是Number的子類,可是對於任意Number集合(如List<Number>)不是Integer集合(如List<Integer>)的超類,由於泛型是不可變的。ci
幸虧java提供了一種叫有限通配符的參數化類型,pushAll參數替換爲「E的某個子類型的Iterable接口」:get
1
2
3
4
|
public
void
pushAll(Iterable<?
extends
E> src){
for
(E e: src)
push(e);
}
|
這樣就能夠正確編譯了,這裏的<? extends E>就是所謂的 producer-extends。這裏的Iterable就是生產者,要使用<? extends E>。由於Iterable<? extends E>能夠容納任何E的子類。在執行操做時,可迭代對象的每一個元素均可以看成是E來操做。it
與之對應的是:假設有一個方法popAll()方法,從Stack集合中彈出每一個元素,添加到指定集合中去。io
1
2
3
4
5
|
public
void
popAll(Collection<E> dst){
if
(!isEmpty()){
dst.add(pop());
}
}
|
假設有一個Stack<Number>和Collection<Object>對象:編譯
1
2
3
|
Stack<Number> numberStack =
new
Stack<Number>();
Collection<Object> objects = ...;
numberStack.popAll(objects);
|
一樣上面這段代碼也沒法經過,解決的辦法就是使用Collection<? super E>。這裏的objects是消費者,由於是添加元素到objects集合中去。使用Collection<? super E>後,不管objects是什麼類型的集合,知足一點的是他是E的超類,因此無論這個參數化類型具體是什麼類型都能將E裝進objects集合中去。
總結:
若是你是想遍歷collection,並對每一項元素操做時,此時這個集合時生產者(生產元素),應該使用 Collection<? extends Thing>.
若是你是想添加元素到collection中去,那麼此時集合時消費者(消費元素)應該使用Collection<? super Thing>
注:此文根據《Effective Java》以及Java Generics: What is PECS? 整理成文。想了解更多有關泛型相關知識,請讀者閱讀《Effective Java》的第五章。