[TOC]
## 泛型类
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224234745.png)
## 泛型接口
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224234848.png)
## 泛型方法
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235032.png)
## 泛型边界
使用 extend 边界符来界定,限制泛型的类型,和下面的通配符不一样
如上面泛型方法所示,使用了 extend 关键字
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235032.png)
这里是在泛型方法中的应用,其实可以用到泛型类或者泛型接口上
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235533.png)
## Super 和 extend 通配符
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200225001445.png)
定义了
动物类 Animal
动物类的子类 Fish 鱼
鱼的子类 Curcian 鲫鱼
动物类的子类 Dog
### 先来看 super 通配符
``` java
// 鱼的集合
List super Fish> list = new ArrayList<>();
list.add(new Fish());
list.add(new Crucian());
```
super Fish> 规定了泛型是 Fish 或者 Fish 的父类。
这个时候,我们只能知道list 是只能存放 Fish 或者 Fish 父类的容器,这个时候如果往里面放 Fish 或者是 Fish 的子类,可以放心放进去。但是 Fish 的父类就不能放进去了,因为编译器不知道会放入Fish 的哪个父类,所以编译器直接禁止用 super 通配符加入超类对象。
同时,编译器只知道list 存的是 Fish 或者Fish 的超类,并不知道存储的是哪个超类,所以 用 super 修饰的泛型列表是允许访问的。
也就是只允许加入 Fish 或者 Fish 的子类对象(多态)。不允许加入Fish 的父类。并且不允许对 list 进行访问
### 再来看 extend 通配符
~~~
// 鱼的集合
List extends Fish> list2 = new ArrayList<>();
// 不能加入
list2.add(new Fish());
list2.add(new Crucian());
// 可以访问
System.out.println(list2.get(0).getName());
System.out.println(list2.get(2).isYouYong());
~~~
super Fish> 规定了泛型是 Fish 或者 Fish 的子类。
这个时候list2 里面存放的只能是 Fish 或者 Fish 的子类,如果我们往里面加入Fish 或者 Fish 的子类,编译器并不知道我们加入是是什么 Fish 的子类,所以编辑器禁止向 extend 修饰的列表加入任何子类,同样的,也不允许加入Fish 本身,事实上不能往被 ? extend Fish 修饰的泛型加入任何值、
但是编译器知道该 list2 中存放的是 Fish 或者 Fish 的子类,所以可以对 list2 进行访问。
## PECS 法则
P - Producor 生产者
E - extends
C - Consumer 消费者
S - super
即:
1. 如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
2. 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那么就不要使用任何通配符。
## 泛型擦除
**泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除**。
比如
~~~
List s1 = new ArrayList<>();
List i1 = new ArrayList<>();
System.out.println(s1.getClass() == i1.getClass());
~~~
返回的结果是 true,所以在运行的时候List 后面的泛型就被擦除了,所以就会出现上面 s1 == i1 的情况
## 下面的可以忽略
super 主要用来添加数据的。
使用 super 修饰的数据,可以添加子类,但是由于不知道是添加的哪个子类,但是访问的时候可能会出问题
extend 主要用来访问数据的
使用 extend 修饰的数据,不可以添加数据,但是可以访问数据
### 通配符 ?
PECS原则:
使用 super 通配符,用来添加数据,只要是属于限定符后面 类的子类都可以添加。
使用 extend 通配符,用来访问数据,因为接收的数据,都可以保证是限定符后面的类的子类,可以任意的访问限定度后面类中的方法。
* 对于`List super Integer> l1`:
* 正确的理解:`? super Integer`限定的是泛型参数. 令 l1 的泛型参数是 T, 则 T 是 Integer 或 Integer 的父类, 因此 Integer 或 Integer 的子类的对象就可以添加到 l1 中.
* 错误的理解:~? super Integer限定的是插入的元素的类型, 因此只要是 Integer 或 Integer 的父类的对象都可以插入 l1 中~
* 对于`List extends Integer> l2`:
* 正确的理解:`? extends Integer`限定的是泛型参数. 令 l2 的泛型参数是 T, 则 T 是 Integer 或 Integer 的子类, 进而我们就不能找到一个类 X, 使得 X 是泛型参数 T 的子类, 因此我们就不可以向 l2 中添加元素. 不过由于我们知道了泛型参数 T 是 Integer 或 Integer 的子类这一点, 因此我们就可以从 l2 中读取到元素(取到的元素类型是 Integer 或 Integer 的子类), 并可以存放到 Integer 中.
* 错误的理解:~? extends Integer 限定的是插入元素的类型, 因此只要是 Integer 或 Integer 的子类的对象都可以插入 l2 中~
## 泛型擦除
**泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除**。