List<? extends Fruit> lst = new ArrayList<Apple>():
可以get()读取,编译器返回Fruit对象 (任何Fruit子类都可以转成Fruit);
无法add()添加 (List<? extends Fruit> 表示具有任何从Fruit继承的类型的List,所以编译器无法确定List中所保存的是Fruit的哪个子类型,就无法安全地向其中添加对象);
List<? super Apple> lst = new ArrayList<Fruit>():
可以get()读取,编译器返回Object对象,且无法强转成Fruit或Apple (编译器认为Apple的父类不一定能转成Fruit或Apple,因为编译器无法确定List中的Apple超类具体是哪一个);
可以add()添加,但只能添加Apple对象或其任何子类(如RedApple对象),不能添加Apple的父类 (编译器无法确定List中所保存的是Apple的哪个父类型)。
PECS:
要从泛型类取数据时,用extends (生产者dest需要能够调用get());
当参数(容器)是一个生产者提供元素给你的代码来用(即容器只读),那么容器的泛型应该使用:
Collection< ? extends T >
要往泛型类写数据时,用super (消费者src需要能够调用add())。
当参数(容器)作为一个消费者来消费你提供的数据(即容器可写),那么容器的泛型应该使用:
Collection< ? super T >
/**
* Copies all of the elements from one list into another. After the
* operation, the index of each copied element in the destination list
* will be identical to its index in the source list. The destination
* list's size must be greater than or equal to the source list's size.
* If it is greater, the remaining elements in the destination list are
* unaffected. <p>
*
* This method runs in linear time.
*
* @param <T> the class of the objects in the lists
* @param dest The destination list.
* @param src The source list.
* @throws IndexOutOfBoundsException if the destination list is too small
* to contain the entire source List.
* @throws UnsupportedOperationException if the destination list's
* list-iterator does not support the {@code set} operation.
*/
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<? extends T> si=src.listIterator();
ListIterator<? super T> di=dest.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
里氏替换原则:子类可以在程序中替换父类对象
Scala一个函数类型的子类,如何保证在里氏替换原则下类型安全:参数要逆变(-),返回值要协变(+).