PECS
来自《Effective Java》的一句话,producer-extends,consumer-super,两者通过规定上下界的形式来做数据限制。
demo
class Fruit {
}
class Apple extends Fruit {
}
class Jonathan extends Apple {
}
class Orange extends Fruit {
}
代码继承关系:
Jonathan extennds Apple
Apple extends Fruit
<? extends T>
限制生产者的数据来源。
可见,使用通配符引入逆变时,使用<? extends T>
指定了泛型的下界为Apple,即只能Apple的父类做List操作才安全,对于Apple的子类是不安全的。
<? super T>
限制消费者数据流入。
在数据流入时,apples可以使用Apple本身的实例和Apple子类放入List中,但是不允许Apple的父类存放,这就限制了数据的使用类型。
// Collections.java
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());
}
}
}
复制的target只能是泛型T的实例对象或泛型T的子类。
复制的src只能是泛型T的实例对象或泛型T的父类。
// Collections.java
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());
}
}
}
PECS代表:producer-extends,consumer-super。
获取和放置原则:
换句话说,如果一个参数化类型代表一个T
生产者,使用<? extends T>
;如果它代表T
消费者,则使用<? super T>
。
read-producer-extends,write-consumer-super.