为什么要有泛型类
在拥有泛型之前,泛型程序设计是由继承实现的。比如:arraylist,在拥有泛型之前arraylist其实只维护一个object引用的数组。
使用继承的问题在于获取值时必须强制类型转换,编译器也无法检查传入参数类型。
所以泛型类的好处在于:使程序拥有更好的安全性和可读性。
定义简单的泛型类
public class A<T>
{
private T first;
private T second;
public A(T first,T second){
this.first=first;
this.second=second;
}
}
类型变量一般使用大写,且比较短。java库中,E代表集合类中的元素类型,K和V分别代表表的关键字与值得类型。T代表任意类型。
泛型类可以看作是普通类工厂。
泛型方法。
可以在普通类中定义一个泛型方法。
class A{
public static <T> T f(T a){
return a;
}
}
类型变量的限定
需要对类型变量加以约束时使用extens,比如:
public static <T extends Comparable> T min(T[]a)...
此时min方法只能被实现了Comparable接口的类的数组调用。限制多个接口时使用&隔开。
泛型代码和虚拟机
类型擦除
泛型类型会被擦除,替换位限定类型,如果没有限定类型则使用Object。所以java中不会出现c++中的模板代码膨胀(每个模板的实例化会产型不通的类型)
为了提升效率,应该将标签接口放在边界列表的尾部,因为如果将非标签接口放在后面。编译器要在必要时插入强制类型转换。
使用类型擦除实现泛型的最主要动机为使泛化的客户端可以使用非泛化的类库,反之亦然,这是迁移兼容性,为了兼容jdk1.5之前的类库,并且保证当一个库转变为泛型之后不会影响到依赖于它的代码和程序.
翻译泛型表达式
擦除返回类型后,编译器要插入强制类型转化。
翻译泛型方法
类型擦除后会有两个不同的方法,所以编译器就要生成桥方法。
总结
- 虚拟机中没有泛型,只有普通的类和方法。
- 所有的类型参数都用他们的限定类型替换
- 桥方法被合成来保持动态
- 为保持类型安全性,必要时插入强制类型转换
约束和局限性
基本是由类型擦除引起的
- 不能用基本类型实例化类型参数
类型擦除后 object不能存储基本类型的值
- 运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的非泛型类型,因此所有的类型查询只产生原始类型。
if(a instanceof Pair<T>)//ERROR
只能测试a是否是一个任意类型的pair
- 不能创建参数化类型的数组
如Pair<String>[] table = new Pair<String>[10];//ERROR
但是可以声明通配类型的数组然后类型转换。
Pair<String>[] table = (Pair<String>[])new Pair<?>[10];
但是结果并不安全,推荐使用ArrayList<Pair<String>>
类型擦除的补偿
可以让泛型类保有一个Class对象,在初始化时传入一个Class对象,转而使用动态的isInstance()方法检测类型代替不可使用的instanceof.
也可以使用class对象.newInstance()创建实例,代替不可用的new关键字