什么是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>.