static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
//apples.add(new Fruit()); // Error
}
List<? super Apple> list1 = new ArrayList<Apple>();
List<? super Apple> list2 = new ArrayList<Fruit>();
List<? super Fruit> list3 = new ArrayList<Fruit>();
//List<? super Fruit> list4 = new ArrayList<Apple>(); //Error
List<? extends Apple> list5 = new ArrayList<Apple>();
//List<? extends Apple> list6 = new ArrayList<Fruit>(); //Error
List<? extends Fruit> list7 = new ArrayList<Fruit>();
List<? extends Fruit> list8 = new ArrayList<Apple>();
1,2,3,4代表持有对象的类型必须 大于等于<? super T> 因为既然是前者的子类 那必然是后者的子类
因此可以添加数据 因为此时编译器已经知道是安全的.
5,6,7,8代表List的类型必须 前者>=后者 针对的是List的关系
- 泛型类型限定为Number以及Number的子类。
Pair<? super Integer>表示: 下界
方法参数接受所有泛型类型为Integer或Integer父类的Pair类型 Pair<?Number>
对比extends和super通配符
作为方法参数,<? extends T>类型和<? super T>类型的区别在于:
- <? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
- <? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。
一个是允许读不允许写,另一个是允许写不允许读。
PECS原则
何时使用extends,何时使用super?为了便于记忆,我们可以用PECS原则:Producer Extends Consumer Super。
即:如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符。
以Collections的copy()方法为例:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i=0; i<src.size(); i++) {
T t = src.get(i); // src是producer 允许读
dest.add(t); // dest是consumer 允许写
}
}
}
无限定通配符
因为<?>通配符既没有extends,也没有super,因此:
- 不允许调用set(T)方法并传入引用(null除外);
- 不允许调用T get()方法并获取T引用(只能获取Object引用)。
换句话说,既不能读,也不能写,那只能做一些null判断:
static boolean isNull(Pair<?> p) {
return p.getFirst() == null || p.getLast() == null;
}