在Java泛型使用中使用“?”作为类型的通配符。《Effective
Java》第28条,使用通配符可以提高API的灵活性。
但是通配符也使得泛型的使用变得更加复杂。
泛型类型是不可变
首先泛型类型是不可变的,比如List< String >就不是List< Object >的子类型,而是两个独立的类型。如下
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; //编译错误,类型不匹配(imcompatible type)
假如可编译通过的话,会带来新的问题,导致运行时错误
objs.add(1); // Here we put an Integer into a list of Strings
String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String
所以为了确保运行时类型安全,Java泛型设计成不可变的。
但是这种不可变的泛型类型,又会带来下面问题:定义一个泛型,为“Collection”增加一个addAll方法,如下实现:
interface Collection<E> ... {
void addAll(Collection<E> items);
}
那么在将“Collection< Object >”中添加“Collection< String >”,该方法就没法使用了,而理论上应该是合法的,因为String是Object的子类,是可以添加到父类的集合中
void copyAll(Collection<Object> to, Collection<String> from) {
to.addAll(from); // 编译不通过:
//Collection<String> 不是 Collection<Object>的子类型
}
为了解决该问题,Java中使用了类型通配符方式来解决,如下
interface Collection<E> ... {
void addAll(Collection<? extends E> items);
}
通配符的上界
? extends T 其中T表示通配符的上界,表示该方法可以接收T以及T的子类。意味着可以安全的读T(所有实例都是T的子类)的实例,但是不能向其添加元素,因为没法确定添加的实例类型跟定义的类型是否匹配。
//Bird Cat都是Animal的子类
public void testAdd(List<? extends Animal> list) {
//下面都会报编译错误的
//因为testAdd方法传入的list的类型不确定的,(有可能是List<Cat>,List<Dog>)就没法确保它可以添加下面的全部类型
//若传入“List<Bird>”,只能add(bird),只有②可以添加
//若传入“List<Dog>”,都不能添加
list.add(new Animal("animal")); //①
list.add(new Bird("bird")); //②
list.add(new Cat("cat")); //③
for(Animal a:list){
//但是可以读取里面的元素,因为他们都是Animal的子类型
}
}
可以理解成:“Collection< String> ”是“Collection< ? extends Object >”的子类型。
这种通配符(wildcard )是通过继承一个范围类(通配符上界,upper bound)来实现类型协变。
通配符的下界
? super T(T表示通配符的下界),表示该方法可以接收T及T的父类参数。意味着可以安全的添加元素,但是不能读取。
public void testAdd2(List<? super Animal> list){
//可以编译通过,因为传入的T类型肯定是Animal的父类,也是Bird,Cat的父类
list.add(new Animal("animal"));
list.add(new Bird("bird"));
list.add(new Cat("cat"));
for(Animal a:list){
//但是不能读取,因为父类不能转换成子类
}
}
可以理解成:“Collection< ? super String > ”是“Collection< String >”的父类型;可以调用集合将String作为参数的方法(如:add(String) or set(int, String));但当从集合中获取元素时,得到是Objec对象而不是String对象。
PECS
PECS stands for Producer-Extends,Consumer-Super
- extends
通配符的上界方式,只能从中读取元素,不能添加元素,形象的称为生产者(Producers)
- super
通配符的下界方式,只能添加元素,没法直接读取下界类型的元素,形象的称为消费者(Consumers)