大家好,我是程序员阿晶,老早之前的文章了,这里直接放这了。

<? extends T>和<? super T>理解与使用

关于此含义的总结描述

? extends T与?super T两者用于泛型对象的读取和插入操作:

? extends T子类限定,被其修饰的对象只能进行读取操作,不能增删。

? super T超类限定,被其修饰的对象只能进行增删操作,不能读取。

List<? extends T>表示该集合中存在的都是类型T的子类,包括T自己。

而List<? super T>表示该集合中存的都是类型T的父类,包括T自己。

List<? extends T>如果去添加元素的时候,因为list中存放的其实是T的一种子类,如果我们去添加元素,其实不知道到底应该添加T的哪个子类,这个时候桥接方法在进行强转的时候会出错。但是如果是从集合中将元素取出来,我们可以知道取出来的元素肯定是T类型。所以? extends T这种方式可以取元素而不能添加,这个叫get原则。

List<? super T>因为存的都是类型T的父类,所以如果去添加T类或者T类子类的元素,肯定是可以的。但是如果将元素取出来,则不知道到底是什么类型,所以? super T可以添加元素但是没法取出来,这个叫put原则。

List<? extends T>

这里T是泛型,而?是通配符,"? extends T"表示T是父类,?是子类,该list只能容纳T类型及T类型的子类。

"? extends T"可以用下图表示

因为界限在"?"的上面,因此将extends称之为上界,即extends给?限定了一个上界。

List<? super T>

"? super T"表示T是子类,?是父类,该list只能容纳T类型及T类型的父类。

"? super T"可以用下图表示

因为界限在"?"的下面,因此将super称之为下界,即super给?限定了一个下界。

实际使用

下面主要来看看List<? extends T>和List<?super T>在实际使用中的例子。

下图是我设计的类继承关系图。

其代码如下:

1
2
3
4
5
6
7
8
9
10
// 物体
class WuTi{}
// 生物
class ShengWu extends WuTi{}
// 动物
class Animal extends ShengWu{}
// 猫
class Cat extends Animal{}
// 狗
class Dog extends Animal{}

先来说说List<? extends T>

假设现在有如下两个集合,lista限定了边界,而listb没有限定边界

1
2
List<? extends Animal> lista = new ArrayList<>();
List<Animal> listb = new ArrayList<>();

显然,listb是我们最常见的用法。

listb由于泛型的类型是确定的(即Animal类型),所以listb能添加Animal、Cat、Dog(因为猫和狗都是动物)。

但是lista的泛型的类型是不确定的(即用通配符?表示的),所以lista无法添加元素,即使添加Object也不行,只能添加null。

lista由于容纳的是Animal及其子类,所以lista能获取元素,并且获取到的元素的类型是Animal类型准没错。

那么既然lista不能添加元素,又何来的获取元素呢?可以用以下代码实现。

虽然List<?extends Animal>不能添加元素,但是可以借助List<Animal>初始化元素

1
2
3
4
5
6
7
8
9
List<Animal> data = newArrayList<>();
data.add(new Dog());
data.add(new Cat());
data.add(newAnimal());
List<? extends Animal> listb = data;
// 获取元素,其元素类型是Animal
Animal a1 =listb.get(0);
Animal a2 =listb.get(1);
Animal a3 = listb.get(2);

总结:由于只能从List<? extends T>中获取元素,而不能向它添加元素,所以称之为生产者。如下图。

接下来说说List<? super T>

1
List<? super Animal> listc = new ArrayList();

此刻我想说的是,listc能够添加元素!

是不是有人就懵了,这跟List<? extends T>不是一样么,都采用通配符?,泛型类型都是不确定的,为什么List<? extends T>不能添加元素,而List<?super T>就能添加元素呢?

我的解答是:listc的泛型类型是不确定的不假,但是List<? super Animal>中的Animal是确定的啊,你想想,假如?表示的是ShengWu类,那么我添加Animal没错吧,因为Animal是生物啊;再假如?表示的是WuTi类,那么我添加Animal也没错,因为Animal是物体啊,哈哈。

另外,既然能添加Animal,那么Animal的子类也能添加,因为猫、狗都是生物或者物体。

所以就有了如下代码

1
2
3
4
5
6
7
8
List<? super Animal> listc = new ArrayList(); 
Animal animal = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();

listc.add(animal);
listc.add(dog);
listc.add(cat);

那么,List<? super T>能获取元素吗?答案是不能,因为无法确定元素的返回值类型到底是啥。

总结:由于只能向List<? super T>添加元素,而不能从它里面获取元素,所以称之为消费者,如下图。

最后

最后扩展一下,PECS(Producer Extends Consumer Super)原则指的就是上述对List<? extends T>和List<? super T>的总结,即生产者对应extends ,而消费者对应super。在实际使用中,我们应当遵循这个原则。