// public final class Integer extends Number
Number num = new Integer(1);
List list = new ArrayList<>();
list.add(new Integer(3));
ArrayList list = new ArrayList(); //type mismatch
List extends Number> list = new ArrayList();
list.add(new Integer(1)); //error
为什么Number的对象可以由Integer实例化,而ArrayList
Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象。而Object的数组类型Object[],理解成是由Object构造出来的一种新的类型,可以认为是一种构造类型,记f(Object),那么可以这么来描述协变和逆变:
当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变;
当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变;
如果上面两种关系都不成立则叫做不可变。
JAVA中泛型是不变的,可有时需要实现逆变与协变,怎么办呢?这时就需要通配符?。
extends>实现了泛型的协变,比如:
List extends Number> list = new ArrayList<>();
“? extends Number”则表示通配符”?”的上界为Number,换句话说就是,“? extends Number”可以代表Number或其子类,但代表不了Number的父类(如Object),因为通配符的上界是Number。
于是有“? extends Number” ≦ Number,则List extends Number> ≦ List< Number >。那么就有:
List extends Number> list001 = new ArrayList();
List extends Number> list002 = new ArrayList();
但是这里不能向list001、list002添加除null以外的任意对象。可以这样理解一下,List
super>实现了泛型的逆变,比如:
List super Number> list = new ArrayList<>();
“? super Number” 则表示通配符”?”的下界为Number。为了保护类型的一致性,因为“? super Number”可以是Object或其他Number的父类,因无法确定其类型,也就不能往List super Number >添加Number的任意父类对象。但是可以向List super Number >添加Number及其子类。
List super Number> list001 = new ArrayList();
List super Number> list002 = new ArrayList
现在问题来了:究竟什么时候用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(Iterable src){
for(E e : src)
push(e)
}
假设有一个实例化Stack
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable extends E> src) {
for (E e : src)
push(e);
}
要实现popAll(Collection
// popAll method without wildcard type - deficient!
public void popAll(Collection dst) {
while (!isEmpty())
dst.add(pop());
}
同样地,假设有一个实例化Stack
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection super E> dst) {
while (!isEmpty())
dst.add(pop());
}
在上述例子中,在调用pushAll方法时生产了E 实例(produces E instances),在调用popAll方法时dst消费了E 实例(consumes E instances)。Naftalin与Wadler将PECS称为Get and Put Principle。
java.util.Collections的copy方法(JDK1.7)完美地诠释了PECS:
public static 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 di=dest.listIterator();
ListIterator extends T> si=src.listIterator();
for (int i=0; i
PECS总结: