Java 泛型: 什麼是PECS(Producer Extends, Consumer Super)

什麼是PECS?java

PECS指「Producer Extends,Consumer Super」。換句話說,若是參數化類型表示一個生產者,就使用;若是它表示一個消費者,就使用,可能你還不明白,不過不要緊,接着往下看好了。code


下面是一個簡單的Stack的API接口:對象

public class  Stack<E>{
    public Stack();
    public void push(E e):
    public E pop();
    public boolean isEmpty();
}

假設想增長一個方法,按順序將一系列元素所有放入Stack中,你可能想到的實現方式以下:接口

public void pushAll(Iterable<E> src){
    for(E e : src)
        push(e)
}

假設有個Stack<Number>,想要靈活的處理Integer,Long等Number的子類型的集合io

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ....;
numberStack.pushAll(integers);

此時代碼編譯沒法經過,由於對於類型Number和Integer來講,雖而後者是Number的子類,可是對於任意Number集合(如List<Number>)不是Integer集合(如List<Integer>)的超類,由於泛型是不可變的。編譯

幸虧java提供了一種叫有限通配符的參數化類型,pushAll參數替換爲「E的某個子類型的Iterable接口」:class

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來操做。泛型

與之對應的是:假設有一個方法popAll()方法,從Stack集合中彈出每一個元素,添加到指定集合中去。object

public void popAll(Collection<E> dst){
       if(!isEmpty()){
                dst.add(pop());
        }
}

假設有一個Stack<Number>和Collection<Object>對象:List

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>.

相關文章
相關標籤/搜索