转自:http://www.cnblogs.com/ttylinux/p/4439503.html
<?extends E> 和<? super E>的使用
1、<? extends E>的使用
如第25条所述,参数化类型是不可变的。换句话说,对于任何两个截然不同的类型Type1和Type2而言,List<Type1>既不是List<Type2>的字类型,也不是它的超类型。
考虑第26条中的堆栈下面就是它的公共API:
publicclass Stack<E>{
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
public voidpushAll(Iterable<E> src);
}
在使用方法pushAll时,添加src,其中的数据类型要跟当前的Stack完全相同,这样才可以使用。比如,Stack<Number>,那么src的类型就只能是Iterable<Number>,不能是Iterable<Integer>。因为,Iterable<Number>与Iterable<Integer>并不是同一个类型。
那么,如果要实现一种效果,可以添加的类型是Stack中元素类型的子类型,而不一定使用完全相同的类型。可以通过有限制的通配符来实现:
public classStack<E>{
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
public voidpushAll(Iterable<? extends E> src);
}
2、<? super E>的使用
publicclass Stack<E>{
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
public voidpushAll(Iterable<? extends E> src);
public voidpopAll(Collection<E> dst);
}
要实现一个功能,将堆栈中的元素弹出来,保存到一个容器中。也就是实现popAll这个API。使用这个API是有要求的,那就是容器的类型要完全跟当前的Stack的类型一致。那么,假设,我们像堆栈中的元素可以存放在容器Collection<Object>中,那么,该如何重新定义popAll方法?
使用super关键字。
publicclass Stack<E>{
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
public voidpushAll(Iterable<? extends E> src);
public voidpopAll(Collection<? super E> dst);
}
修改后的popAll方法,可以保存堆栈弹出的元素的容器类型是这样的,容器类型是Stack元素类型的父类。
3、使用了上述的通配符,会提高API的灵活性,让它可以接受更多的类型。
1)pushAll 是数据的生产者:对生产者的进参数使用 <? extends E>,可以接受更多的类型,而不是只是E这种类型,可以接受E及其子类的类型。
2) popAll 是数据的消费者:对消费者出参数使用<? super E>,可以让堆栈的数据保存在多种类型的容器中,而不只是保存在Collection<E>。它可以保存在类型是E的父类的容器中。