泛型比使用Object变量,然后进行强制类型转换具有更好的安全性和可读性,对于集合类尤其有用。
12.1 为什么要使用泛型程序设计
泛型意味着编写的代码可以被很多不同类型的对象所重用。
ArrayList类有一个类型参数用来指示元素的类型:ArrayList<String> files;代码具有很好的可读性,一看就知道这个数组列表中包含的是String对象。编译器也可以很好地利用这个信息,当调用get的时候,不需要进行强制类型转换,编译器就知道返回值类型为String,而不是Object。
通配符类型。
12.2 简单泛型类的定义
一个泛型类ius具有一个或多个类型变量的类。Java中较常见是类型变量使用大写形式,使用E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T表示任意类型。
public class Pair<T>{
private T first; private T second;
public Pair(){ first = null; second = null; }
public Pair(T first, T second){ this.first = first; this.second = second; }
public T getFirst(){ return first; }
public T getSecond(){ return second; }
public void setFirst(T first){ this.first = first; }
public void setSecond(T second){ this.second = second; }
}
泛型类可以看作普通类的工厂,用具体的类型替换类型变量就可以实例化泛型类型,如Pair<String> ,Pair<String>(),String getFirst().
12.3 泛型方法
class ArrayAlg{
public static <T> T getMiddle( T[] a ) { return a[a.length/2]; }
}
泛型方法可以定义在普通类中,也可以定义在泛型类中。类型变量放在修饰符的后面,返回类型的前面。
调用泛型方法时,在方法名前的尖括号中放入具体的类型:String middle = ArrayAlg.<String> getMiddle(files);也可以直接调用 String middle= ArrayAlg.getMiddle(files);
12.4 类型变量的限定
类或方法需要对类型变量加以限定,如将T限制为实现了Comparable接口的类:public static <T extends Comparable> T min(T[] a);,此时T所属的类必须具有compareTo方法。
<T extends BoundingType>,表示T应该是绑定类型的子类型,T和绑定类可以是类或接口。
<T extends Comparable & Serializable>,可以有多个限定,限定类型用&分隔。
12.5 泛型代码和虚拟机
虚拟机没有泛型类型对象,所有对象都属于普通类。
捂脸何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定的变量用Object替换)。如果有多个限定的类型,就用第一个限定类型替换。
public class Pair{
public Pair(Object first, Object second){}
public Object getFirst(){return first;}
public void setFirst(Object first){ this.first = first; }
private Object first;
}
12.5.1 翻译泛型表达式
当程序调用泛型方法时,编译器自动插入强制类型转换,即翻译为两条虚拟机指令:1.对原始方法的调用,2.将返回的Object类型强制转换。
12.5.2 翻译泛型方法
编译器生成桥方法来保证多态。
12.5.3 调用遗留代码
12.6 约束与局限性
12.6.1 不能用基本类型实例化类型参数
类型擦除后,Pair类含有Object类型的域,Object不能存储double、int等基本类型的值,只有Pair<Double>。
12.6.2 运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的类型(非泛型),所有的类型查询只产生原始类型:a instanceof Pair<String> 等价于a instanceof Pair。
getClass方法总返回原始类型:Pair<String> str; Pair<Employee> emp; str.getClass()==emp.getClass()返回true,因为两次调用getClass都返回Pair.class。
12.6.3 不能抛出也不能捕获泛型类实例
泛型类扩展Throwable不合法,不能在catch子句中使用类型变量,catch(T e)错误。
12.6.4 参数化类型的数组不合法
如Pair<String>[] table = new Pair<String>[10];,将不能通过编译。
12.6.5 不能实例化类型变量
不能使用new T(...),new T[...],或T.class。
Class类本身是泛型,如String.class是一个Class<String>的实例,也是唯一实例。
不能构造一个泛型数组,new T[2],类型擦除会永远构造Object[2]数组。
12.6.6 泛型类的静态上下文中类型变量无效
不能在静态域或静态方法中引用类型变量。类型擦除后,将只剩下原始类型。
12.6.7 注意擦除后的冲突
12.7 泛型类型的继承规则
Manager是Employee的一个子类,但Pair<Manager>不是Pair<Employee>的子类。
无论S与T有什么联系,Pair<S>与Pair<T>没有什么联系。但是可以将参数化类型转换为一个原始类型,如Pair<Employee>是原始类型Pair的一个子类型。
泛型类可以扩展或实现其他的泛型类。如ArrayList<T>类实现List<T>接口,一个ArrayList<Employee>可以转换为一个List<Employee>。
12.8 通配符类型
Pair<? extends Employee>,表示任何泛型Pair的类型,它的类型参数是Employee的子类,如Pair<Manager>。
类型Pair<Manager> 是Pair<? extends Employee>的子类型。
12.8.1 通配符的超类型限定
12.8.2 无限定通配符
12.8.3 通配符捕获
12.9 反射和泛型
12.9.1 使用Class<T>参数进行类型匹配
12.9.2 虚拟机中的泛型类型信息
本文介绍了Java中的泛型程序设计,包括为何使用泛型、简单泛型类定义、泛型方法、类型变量限定,以及泛型在虚拟机中的表现和约束局限。泛型提供了更好的类型安全性和可读性,减少了强制类型转换的需要,尤其是在集合类中。同时,文章还讨论了泛型的继承规则、通配符类型和反射的应用。
967

被折叠的 条评论
为什么被折叠?



