概念
PECS即 Producer extends Consumer super
即如果参数化类型表示一个T的生产者,使用<? extends T>,如果表示一个T的消费者,使用<? super T>。
再通俗点说:
- 从集合中取元素, 使用<? extends T>通配符
- 向集合中放元素, 使用<? super T>通配符
问题
这里仍然使用网上常见的一个例子,Fruit、Apple、RedApple的例子
继承关系为:
RedApple extends Apple
Apple extends Fruit
首先明确一个问题:为什么会出现泛型通配符?
Fruit fruit = new Apple();//没有问题
List<Fruit> list = new ArrayList<Apple>();//编译不通过
因为List<Fruit>不是List<Apple>的父类
泛型中List的父类需要用List<? extends Apple>表示
<? extends T>
看一个例子:
从上面代码中可以看出,向fruitContainer中是不能添加元素的, 但是可以取出Fruit类型的元素
可以这样理解:
- 泛型就是一个标签,容器上贴上什么标签就只能放对应类别的东西,< Apple >:苹果容器,< Fruit >:水果容器,<? extends Fruit>:某种水果容器
- 苹果容器(appleList)只能装苹果,不能装其它水果的,水果容器(fruitList)是可以装各种水果
- 将一个水果容器(fruitList)贴上<? extends Fruit>标签以后,我们只知道它是放某种水果的容器,并不知道是放那种水果的容器,有可能是放苹果、也有可能是放香蕉的、还有可能是所有水果都能放的
- 所以要对贴<? extends Fruit>标签的容器进行限制,不允许随意放入水果,假如是苹果容器,结果用户想放入香蕉呢,往外拿是可以的,因为我们知道拿出来的一定是Fruit
<? super T>
- 代码中可以看出appleContainer是可以往里放元素的,放的元素只能是Apple以及其子类,取出的元素只能用Object接纳容器在贴上<? super T>标签后,我们只知道它是存放Apple或者是某种父类水果的容器
- 放入水果时,只要是Apple以及子类都可以放心放入,但是Apple的父类就不能保证一定能放进去了,如果原来是Fruit容器还好,如果是Apple容器,当然不能放入Fruit啊,所以Apple的父类干脆都不让放
- 取水果时从里面取出来的可能是Apple也可能是其父类,不能确定到底是什么所以只能用顶级父类Object存放