2019独角兽企业重金招聘Python工程师标准>>>
PECS
现在问题来了:我们什么时候用extends什么时候用super呢?《Effective Java》给出了答案:
PECS: producer-extends, consumer-super
比如,一个简单的Stack API:
public class Stack{ public Stack(); public void push(E e): public E pop(); public boolean isEmpty(); }
要实现pushAll(Iterable
public void pushAll(Iterablesrc){ for(E e : src) push(e) }
假设有一个实例化Stack
在调用pushAll方法时会发生type mismatch错误,因为Java中泛型是不可变的,Iterable
因此,应改为
// Wildcard type for parameter that serves as an E producer public void pushAll(Iterable extends E> src) { for (E e : src) // out T, 从src中读取数据,producer-extends push(e); }
要实现popAll(Collection
// popAll method without wildcard type - deficient! public void popAll(Collectiondst) { while (!isEmpty()) dst.add(pop()); }
同样地,假设有一个实例化Stack
调用popAll方法是会发生type mismatch错误,因为Collection
因而,应改为:
// Wildcard type for parameter that serves as an E consumer public void popAll(Collection super E> dst) { while (!isEmpty()) dst.add(pop()); // in T, 向dst中写入数据, consumer-super }
Naftalin与Wadler将PECS称为 Get and Put Principle。
在java.util.Collections
的copy
方法中(JDK1.7)完美地诠释了PECS:
public staticvoid 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 di=dest.listIterator(); // in T, 写入dest数据 ListIterator extends T> si=src.listIterator(); // out T, 读取src数据 for (int i=0; i 6.3 Kotlin的泛型特色
正如上文所讲的,在 Java 泛型里,有通配符这种东西,我们要用
? extends T
指定类型参数的上限,用? super T
指定类型参数的下限。而Kotlin 抛弃了这个东西,引用了生产者和消费者的概念。也就是我们前面讲到的PECS。生产者就是我们去读取数据的对象,消费者则是我们要写入数据的对象。这两个概念理解起来有点绕。
我们用代码示例简单讲解一下:
public staticvoid copy(List super T> dest, List extends T> src) { ... ListIterator super T> di = dest.listIterator(); // in T, 写入dest数据 ListIterator extends T> si = src.listIterator(); // out T, 读取src数据 ... }
List super T> dest
是消费(方法产生)数据的对象,这些数据会写入到该对象中,这些数据该对象被“吃掉”了(Kotlin中叫in T
)。
List extends T> src
是(为方法)提供数据的对象。这些数据哪里来的呢?就是通过src读取获得的(Kotlin中叫out T
)。6.3.1
out T
与in T
在Kotlin中,我们把那些只能保证读取数据时类型安全的对象叫做生产者,用
out T
标记;把那些只能保证写入数据安全时类型安全的对象叫做消费者,用in T
标记。如果你觉得太晦涩难懂,就这么记吧:
out T
等价于? extends T
in T
等价于? super T
此外, 还有*
等价于?