1. static void writeTo(List<? super Apple> apples) {
  2. apples.add(new Apple());
  3. apples.add(new Jonathan());
  4. //apples.add(new Fruit()); // Error
  5. }
  1. List<? super Apple> list1 = new ArrayList<Apple>();
  2. List<? super Apple> list2 = new ArrayList<Fruit>();
  3. List<? super Fruit> list3 = new ArrayList<Fruit>();
  4. //List<? super Fruit> list4 = new ArrayList<Apple>(); //Error
  5. List<? extends Apple> list5 = new ArrayList<Apple>();
  6. //List<? extends Apple> list6 = new ArrayList<Fruit>(); //Error
  7. List<? extends Fruit> list7 = new ArrayList<Fruit>();
  8. 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()方法为例:

  1. public class Collections {
  2. public static <T> void copy(List<? super T> dest, List<? extends T> src) {
  3. for (int i=0; i<src.size(); i++) {
  4. T t = src.get(i); // src是producer 允许读
  5. dest.add(t); // dest是consumer 允许写
  6. }
  7. }
  8. }

无限定通配符

因为<?>通配符既没有extends,也没有super,因此:

  • 不允许调用set(T)方法并传入引用(null除外);
  • 不允许调用T get()方法并获取T引用(只能获取Object引用)。

换句话说,既不能读,也不能写,那只能做一些null判断:

  1. static boolean isNull(Pair<?> p) {
  2. return p.getFirst() == null || p.getLast() == null;
  3. }