hello云胜

技术与生活

0%

List、List、List<?> 的三者的区别以及 <? extends T>与<? super T> 的区别

终于搞明白了<? extends T>与<? super T>的区别,网上很多文章说的都是错误的,下面讲下我的理解。

List、List、List<?>

List :完全没有类型限制和赋值限定。
List :看似用法与List一样,但是在接受其他泛型赋值时会出现编译错误。
List:是一个泛型,在没有赋值前,表示可以接受任何类型的集合赋值,但赋值之后不能往里面随便添加元素,但可以remove和clear,并非immutable(不可变)集合。List一般作为参数来接收外部集合,或者返回一个具体元素类型的集合,也称为通配符集合。
代码验证:

image-20200402212520699

image-20200402212551190

<? extends T>与<? super T>

List 最大的问题是只能放置一种类型,为了放置多种受泛型约束的类型,出现了 <? extends T>与<? super T> 两种语法。简单来说, <? extends T> 是Get First,适用于取出集合元素消费的场景;<? super T>是Put First,适用于生产集合元素为主的场景。

: 里面放的是T或T的子类元素,注意理解,是给集合设置了一个界限,不能往里放T以上的类型,但是可以安全的插入T或T子类。因为有上界,可以安全的插入上界类型的子类。但是读取出来时,因为无法确定具体是什么子类型,所以只能统一转为上届类型,泛型丢失。 :里面放的也是T或T的子类。那可能你会疑惑,这又什么区别呢?, 一般是用作方法的入参,用来限制传进来的参数必须只能是某个T类或其子类,这样我的方法可以统一向上强制转型为T进行处理。任何元素都不得添加进集合内。记住不能add元素进去。因为我们并不知道传进来的是什么具体的子类。 代码示例如下,以加菲猫、猫、动物为例,说明extends和super的详细语法差异 ![image-20200402212621339](D:\文章\java\基础\image-20200402212621339.png) ![image-20200402212643981](D:\文章\java\基础\image-20200402212643981.png) ![image-20200402212659097](D:\文章\java\基础\image-20200402212659097.png) 总之,的场景是put功能受限,可以读取元素,一般用来接收参数。 而是可以插入元素。 源码放上:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static void main(String[] args) {

//第一段:泛型出现之前集合定义方式
List a1 =new ArrayList();
a1.add(new Object());
a1.add(new Integer(10));
a1.add(new String("string"));

//第二段:把a1引用赋值给a2,(a2与a1的区别是增加了泛型限制)
List<Object> a2 =a1;
a2.add(new Object());
a2.add(new Integer(20));
a2.add(new String("string2"));
a2.add(25);
//List<Object> 接受其他泛型赋值时,会报异常(因为在下面例子中List<Integer>不能转为List<Object>)
List<Integer> aint = new ArrayList<Integer>();
List<Object> a22 =aint;//Type mismatch: cannot convert from List<Integer> to List<Object>

//第三段:把a1引用赋值给a3,(a3与a1的区别是增加了泛型<Integer>)
List<Integer> a3 = a1; //此时如果遍历a3则会报类型转换异常ClassCastException
a3.add(new Integer(20));
//下面两行编译出错,不允许增加非Integer类型进入集合
a3.add(new Object());//The method add(Integer) in the type List<Integer> is not applicable for the arguments (Object)
a3.add(new String("string2"));

//第四段:把a1引用赋值给a4,a4与a1的区别是增加了通配符
List<?> a4 = a1;
//允许删除和清除元素
a4.remove(0);
a4.clear();
//编译错误,不允许添加任何元素
a4.add(new Object());
a4.add(new Integer(20));
a4.add(new String("string2"));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Animal{}
class Cat extends Animal{}
class Garfield extends Cat{}

//用动物,猫,加菲猫的继承关系说明extends与super在集合中的意义
public class AnimalCatGarfield {
public static void main(String[] args) {
//第一段:声明第三个依次继承的集合:Object>动物>猫>加菲猫 三个泛型集合可以理解为三个不同的笼子
List<Animal> animal = new ArrayList<Animal>(); //动物
List<Cat> cat = new ArrayList<Cat>(); //猫
List<Garfield> garfield = new ArrayList<Garfield>(); //加菲猫

animal.add(new Animal());
cat.add(new Cat());
garfield.add(new Garfield());

//第二段:测试赋值操作 以Cat为核心,因为它既有子类又有父类
//下行编译出错。只能赋值Cat或Cat子类集合
List<? extends Cat> extendsCatFromAnimal = animal;
List<? super Cat> superCatFromAnimal = animal;

List<? extends Cat> extendsCatFromCat = cat;
List<? super Cat> superCatFromCat = cat;

List<? extends Cat> extendsCatFromGarfield = garfield;
//下行编译出错。只能赋值Cat或着Cat父类集合
List<? super Cat> superCatFromGarfield = garfield;

//第三段:测试add方法
//下面三行中所有的<? extends T>都无法进行add操作,编译出错
extendsCatFromCat.add(new Animal());
extendsCatFromCat.add(new Cat());
extendsCatFromCat.add(new Garfield());

//下行编译出错。只能添加Cat或者Cat的子类集合。
superCatFromCat.add(new Animal());
superCatFromCat.add(new Cat());
superCatFromCat.add(new Garfield());

//第四段:测试get方法
//所有的super操作能够返回元素,我们可以安全的确定,superCatFromCat里的元素一定是CAT或其子类
// 所以可以进行一定的强制类型转换
//可以强转为Animal 安全
Animal object = (Animal) superCatFromCat.get(0);
//可以强转为CAT 安全
Cat object3 = (Cat) superCatFromCat.get(0);
//也能强转为加菲猫,但是不安全
Garfield object4 = (Garfield) superCatFromCat.get(0);
//以下extends操作能够返回元素
Animal catExtends3 = extendsCatFromCat.get(0);
Object catExtends2 = extendsCatFromCat.get(0);
Cat catExtends1 = extendsCatFromCat.get(0);
//下行编译错误。虽然Cat集合从Garfield赋值而来,但类型擦除后,是不知道的
// 可以进行强转,但是不安全。
Garfield cat2 = (Garfield) extendsCatFromGarfield.get(0);
}
}