泛型是一种 "代码模板"
什么是泛型
T 可以是任何 class. 编写一次模板, 创建任意类型的 ArrayList
泛型是定义一种模板, 例如: ArrayList<T>, 然后再代码中为用到的类创建 ArrayList < 类型>
编写一次, 万能匹配, 通过了编译器又保证了类型安全
向上转型
通过应用接口, 实现向上转型
ArrayList<T> implements List<T>
: ArrayList<T > 向上转型为 List<T>
模板之间没有任何继承关系: ArrayList<Integer > 和
ArrayList<Number > 两者完全没有继承关系
在向上继承时, T 不可变!
使用泛型
使用时, 如果不定义泛型类型, 默认是 Object.
如果泛型定义为 Object 没有发挥泛型的优势.
编译器自动推断, 可以省略
List<Number> list = new ArrayList<>;
泛型接口
可以在接口中定义泛型类型, 实现此类接口必须实现正确的泛型类型
编写泛型类
一般来说, 泛型类用在集合类中, 我们很少编写泛型类
就是这个类, 可以针对某个特定的类型, 成为一个专门的类
在类后面定义一个 < T>, 然后在这个类里面就有了这么一种类型 T, 任意使用
泛型类型
<T > 不能用于静态方法
静态方法, 可以单独改写为 "泛型" 方法.
静态方法的泛型, 和实例类型的泛型不是一个, 应该清楚的区分开
- public class Pair<T> {
- private T first;
- private T last;
- public Pair(T first, T last) {
- this.first = first;
- this.last = last;
- }
- public T getFirst() {
- return first;
- }
- public T getLast() {
- return last;
- }
- // 对于静态方法使用 < T>
- public static<K> Pair<K> create(K first, K last) {
- return new Pair<K>(first, last);
- }
- }
多个反省类型
泛型可以定义多种类型.<T, K>
使用的使用, 分别指出两种类型即可
Pair<String, Integer>
擦拭法
泛型是一种模板技术, 不同语言实现方式不同, java 是擦拭法
擦拭法: 虚拟机对泛型其实一无所知, 所有的工作都是编译器做的
Java 的泛型是由编译器在编译时实行的, 编译器内部永远吧所有类型 T 视为 Object 处理.
在需要转型的时候, 编译器会根据 T 的类型, 自动为我们实行安全地强制类型.
java 发型的局限
<T > 不能是基本类型, 例如 int, 因为实际类型是 Object.
无法取得带泛型的 Class: <T > 是 Object, 无论 T 的类型是什么, getClass()返回同一个 Class 实例
无法判断带泛型的 Class: 不存在 Pair<String>.class, 只存在唯一的 Pair.class
不能实例化 T 类型: 只能通过反射传入实现.
不恰当的覆写方法
定义的 equals(T t)不会覆写成功, 因为摩擦成 equals(Object t), 这就是一个覆写方法了
泛型继承
- class IntPair extends Pair<Integer> {
- public IntPair(Integer first, Integer last) {
- super(first, last);
- }
- }
- public class Pair<T> {
- private T first;
- private T last;
- public Pair(T first, T last) {
- this.first = first;
- this.last = last;
- }
- }
- // 可以直接使用: Integer ip = new IntPair(1, 2)
- // 获取继承泛型类型方法
- Class<IntPair> clazz = IntPair.class;
- Type t = clazz.getGenericSuperclass();
- if (t instanceof ParameterizedType) {
- ParameterizedType pt = (ParameterizedType) t;
- Type[] types = pt.getActualTypeArguments();
- Type firstType = types[0];
- Class<?> typeClass = (Class<?>) firstType;
- System.out.println(typeClass);
- }
因为 java 中引入了泛型, 所以只用 Class 来标识泛型不够用. 所以: java 类型的体系如下:
┌────┐
│Type│
└────┘
▲
│
┌────────────┬────────┴─────────┬───────────────┐
│ │ │ │
┌─────┐┌─────────────────┐┌────────────────┐┌────────────┐
│Class││ParameterizedType││GenericArrayType││WildcardType│
└─────┘└─────────────────┘└────────────────┘└────────────┘
extends 通配符
Pair<Integer > 类型, 符合参数
Pair<? extends Number>
: 上界通配符, 类型 T 上界限定在 Number
方法参数签名
setFirst(? extends Number)
无法传递任何 Number 类型给
setFirst(? extends Number)
唯一的例外可以传入 null, // ok, 但是后面排除
NullPointerException
extends 通配符的作用
可以通过 get 获取一个指定类型的返回结果
无法通过 set 进行设置和修改类型
使用 extends 通配符表示可以读, 不能写.
使用 extends 限定 T 类型
在定义泛型类型 Pair<T > 的时候, 可以使用 extends 通配符来限定 T 的类型
super 通配符
允许
set(? super Integer)
传入 Integer 的引用
不允许调用 get()方法获得 Integer 的引用
使用<? super Integer > 通配符作为方法参数, 便是方法内部代码对于参数只能写, 不能读
对比 extends 和 super 通配符
- <? extends T > 允许调用方法 T get()获取 T 的引用, 但不允许调用方法 set(T)传入 T 的引用(传入 null 除外)
- <? super T > 允许调用方法 set(T)传入 T 的引用, 但不允许调用方法 T get()获取 T 的引用(获取 Object 除外)
- class Collections {
- public static <T> void copy(List<? super T> dest, List<? extends T> src) {
- // 编辑器可以避免: 意外读取 dest, 意外操作 src. 安全操作数组
- for (int i = 0; i <src.size(); i++) {
- T t = src.get(i);
- dest.add(t);
- }
- }
- }
PECS 原则
Producer Extends Consumer Super
如果需要返回 T, 它是生产者(Producer), 需要使用 extends 通配符
如果需要写入 T, 它是消费者(Consumer), 需要使用 super 通配符
需要返回 T 的 src 是生产者, 声明为<? extends T>
需要写入 T 的 dest 是消费者, 声明为<? super T>
无限定通配符
无限定通配符(Unbounded Wildcard Type), 即只定义一个?
- void sample(Pair<?> p) {
- }
- <?>具体作用:
不允许调用 set<T > 方法传入引用 T(null 除外)
不允许调用 T get<>方法并获取 T 引用(只能获取 Object 引用)
换句话说: 既不能读, 也不能写, 只能做一些 null 判断
只做一些 null 判断, 大多数情况下, 引入泛型参数
<T > 消除 <?> 通配符
Pair<?>是所有的 Pair<T>
的超类
- Pair<Integer> p = new Pair<>(123, 456);
- Pair<?> p2 = p; // 安全的向上转型
泛型和反射
Class<T > 就是泛型
调用 Class 的 getSuperclass()方法返回的 Class 类型是 Class<? super T>
构造方法 Constructor<T > 也是泛型
- Class<Integer> clazz = Integer.class;
- Constructor<Integer> cons = clazz.getConstructor(int.class);
- Integer i = cons.newInstance(123);
可以声明但泛型的数组, 但不能用 new 操作符创建, 必须通过强制转型实现.
- // Pair<String>[] ps = null; // ok
- // Pair<String>[] ps = new Pair<String>[2]; // Cannot create a generic array of Pair<String>
- Pair<String>[] ps = (Pair<String> []) new Pair[2];
使用泛型数组, 是有风险的, 必须扔到初始数组的引用, 直接进行强制转换
- class ABC<T> {
- // T[] createArray() {
- // return new T[5];
- // }
- T[] createArray(Class<T> cls) { // 借助 `Class<T > 创建泛型数组 `
- return (T[]) Array.newInstance(cls, 5);
- }
- }
谨慎使用泛型可变参数
疑问
int 和 Integer 两种类型的关系和区别?
谨慎使用泛型可变参数中, 为什么第二个会报错的原因没看懂.
来源: http://www.bubuko.com/infodetail-3496864.html