PECS 代表: producer-extends,consumersuper

即如果参数化类型表示一个T的生产者,使用<? extends T>,如果表示一个T的消费者,使用<? super T>。
再通俗点说:

  • 从集合中取元素, 使用<? extends T>通配符
  • 向集合中放元素, 使用<? super T>通配符

    问题

    这里仍然使用网上常见的一个例子,Fruit、Apple、RedApple的例子
    继承关系为:
    RedApple extends Apple
    Apple extends Fruit
    首先明确一个问题:为什么会出现泛型通配符?
  1. Fruit fruit = new Apple();//没有问题
  2. List<Fruit> list = new ArrayList<Apple>();//编译不通过
  3. 因为List<Fruit>不是List<Apple>的父类

泛型中List的父类需要用List<? extends Apple>表示

<? extends T>

看一个例子:
31.使用限定通配符来增加 API 的灵活性 - 图1
从上面代码中可以看出,向fruitContainer中是不能添加元素的, 但是可以取出Fruit类型的元素

可以这样理解:

泛型就是一个标签,容器上贴上什么标签就只能放对应类别的东西,< Apple >:苹果容器,< Fruit >:水果容器,<? extends Fruit>:某种水果容器
苹果容器(appleList)只能装苹果,不能装其它水果的,水果容器(fruitList)是可以装各种水果
将一个水果容器(fruitList)贴上<? extends Fruit>标签以后,我们只知道它是放某种水果的容器,并不知道是放那种水果的容器,有可能是放苹果、也有可能是放香蕉的、还有可能是所有水果都能放的
所以要对贴<? extends Fruit>标签的容器进行限制,不允许随意放入水果,假如是苹果容器,结果用户想放入香蕉呢,往外拿是可以的,因为我们知道拿出来的一定是Fruit

<? super T>

31.使用限定通配符来增加 API 的灵活性 - 图2
代码中可以看出appleContainer是可以往里放元素的,放的元素只能是Apple以及其子类,取出的元素只能用Object接纳容器在贴上<? super T>标签后,我们只知道它是存放Apple或者是某种父类水果的容器
放入水果时,只要是Apple以及子类都可以放心放入,但是Apple的父类就不能保证一定能放进去了,如果原来是Fruit容器还好,如果是Apple容器,当然不能放入Fruit啊,所以Apple的父类干脆都不让放
取水果时从里面取出来的可能是Apple也可能是其父类,不能确定到底是什么所以只能用顶级父类Object存放