Create by westfallon on 8/13
为什么要使用泛型程序设计
- 泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象重用
定义简单的泛型类
- 一个泛型类(generic class)就是具有一个或多个类型变量的类
- 在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型。T(需要时也可以使用U和S)表示任意类型
泛型方法
- 泛型方法可以定义在普通类中,也可以定义在泛型类中
- 当调用一个泛型方法时,在方法名前的尖括号放入具体的类型
- 可以对类型变量设置限定(bound)
通过设置限定确保了T实现了Comparable这个接口public static <T extends Comparable> T min(T[] a)... - 限定类型用 & 分隔,逗号用来分隔变量类型
泛型代码和虚拟机
- 虚拟机没有泛型类型对象,所有对象都属于普通类
- 无论何时定义一个泛型类型,都自动提供了一个响应个原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object)
- Java泛型与C++泛型有很大区别。C++中每个模版的实例化产生不同的类型,这一现象称为“模版代码膨胀”
泛型转换
- 虚拟机中没有泛型,只有普通的类和方法
- 所有的类型参数都用它们的限定类型替换
- 乔方法被合成来保持多态
- 为保持类型安全性,必要时插入强制类型转换
约束与局限性
- 不能用类型参数代替基本类型,原因是类型擦除
- 虚拟机中的对象总有一个特定的非泛型类型,因此所有的类型查询只产生原始类型,同样的道理,getClass方法总是返回原始类型
- 不能实例化参数化类型的数组,如果需要收集参数化类型对象,只有一种安全而有效的方法:使用ArrayList
ArrayList<Pair<String>> - 不能使用
new T(...),new T[...]或T.class这样的表达式中的类型变量 - 不能在静态域或方法中引用类型变量
- 既不能抛出也不能捕获泛型类对象
- 当泛型类型被擦除时,无法创建引发冲突的条件
- 要想支持擦除的转换,就需要强行限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化
继承规则
- 无论T和S有什么联系,
Pair<S>与Pair<T>没有联系
反射和泛型
- Class类是泛型的。例如,
String.class实际上是一个Class<String>类的对象 - 可以使用反射API来确定:
- 这个泛型方法有一个叫做T的类型参数
- 这个类型参数有一个子类型限定,其自身又是一个泛型类型
- 这个限定类型有一个通配符参数
- 这个通配符参数有一个超类型限定
- 这个泛型方法有一个泛型数组参数