[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<String> s1 = new ArrayList<>();
List<Integer> 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 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除**。
- Java 面试题
- String、StringBuffer、StringBuilder 的区别?
- Java 中的四种引用
- 接口和抽象类的本质区别
- 集合框架
- 集合概述
- ArrayList 源码分析
- LinkedList 源码分析
- HashMap 源码分析
- LinkedHashMap 源码分析
- Android提供的 LruCache 的分析
- LinkedList 和 ArrayList 的区别
- 多线程
- 实现多线程的几种方式
- 线程的几种状态
- Thread 的 start() 和 run() 的区别
- sleep() 、yield() 和 wait() 的区别 ?
- notify() 和 notifyAll() 的区别?
- 保证线程安全的方式有哪几种?
- Synchronized 关键字
- volatile 和 synchronized 的区别?
- 如何正确的终止一个线程?
- ThreadLocal 原理分析
- 线程池
- 多线程的三个特征
- 五种线程池,四种拒绝策略,三种阻塞队列
- 给定三个线程如何顺序执行完以后在主线程拿到执行结果
- Java 内存模型
- 判定可回收对象算法
- equals 与 == 操作符
- 类加载机制
- 类加载简单例子
- 算法
- 时间、空间复杂度
- 冒泡排序
- 快速排序
- 链表反转
- IO
- 泛型
- Kolin 面试题
- Android 面试题
- Handler 线程间通信
- Message、MessageQueue、Looper、Handler 的对象关系
- Handler 使用
- Handler 源码分析
- HandlerThread
- AsyncTask
- IntentService
- 三方框架
- Rxjava
- rxjava 操作符有哪些
- 如何解决 RxJava 内存泄漏
- Rxjava 线程切换原理
- map和 flatmap 的区别
- Databinding引起的 java方法大于 65535 的问题
- Glide
- Glide 的缓存原理
- Glide 是如何和生命周期绑定的?不同的Context 有什么区别?
- Glide 、Picasso 、的区别,优劣势,如何选择?
- Jetpack
- 源码分析
- EventBus
- EventBus 源码分析
- RxBus 替代 EventBus
- OkHttp
- OkHttp 源码分析
- OkHttp 缓存分析
- RxPermission
- RxPermission 源码分析
- Retrofit
- create
- Retrofit 源码分析
- 优化
- 启动优化
- 布局优化
- 绘制优化
- 内存优化
- 屏幕适配
- 组件
- Activity
- Frgment
- Service
- ContentProvider
- BroadcastReceiver
- 进程间通信
- Binder机制和AIDL
- AILD 中的接口和普通的接口有什么区别
- in、out、inout 的区别
- Binder 为什么只需要拷贝一次
- 在android中,请简述jni的调用过程
- 生命周期
- Activity 生命周期
- Fragment 生命周期
- Service 生命周期
- onSaveInstanceState() 与 onRestoreIntanceState()
- 前沿技术
- 组件化
- 模块化
- 插件化
- 热更新
- UI - View
- Android 动画
- 事件分发机制
- WebView
- 系统相关
- 谈谈对 Context 的理解
- Android 版本
- App应用启动流程
- App 的打包
- App 的加固
- App 的安装
- Activity 启动流程
- ClassLoader
- Lru 算法加载 Bitmap 三级缓存原理
- Parcelable 和 Serializable 的区别
- Activity的启动流程
- 相关概念
- 网络相关
- Http
- Https
- Http 和 Https 的区别
- 为什么要进行三次握手和四次挥手?
- OkHttp使用Https访问服务器时信任所有证书
- 设计模式
- 单例模式
- 构建者模式
- 工厂模式
- 外观模式
- 代理模式