上一篇:泛型程序设计语法
要讨论泛型的约束和局限性,必须先了解Java的类型擦除。
Java虚拟机内没有泛型类型对象----所有对象都属于普通类。因此无论何时定义一个泛型类型,都自动提供一个相应的原始类型。原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定的变量用Object)。例如:
public class Interval<T extends Comparable & Serializable> implements Serializable{
private T lower;
public Interval(T first, T second){...}
}
//原始类型如下:
public class Interval implements Serializable{
private Comparable lower;
public Interval(Comparable first, Comparable second){...}
}
泛型方法也存在类型擦除,其工作原理和泛型类的擦除一致。
总之,Java的类型擦除需要记住4点:
1、不能用基本数据类型替代类型参数。
没有Piar<double>,只有Piar<Double>。其原因是类型擦除,擦除之后Piar类只有Object类型域,而Object不能存储double。语法上是这样,但鉴于Java提供了自动装箱拆箱,实际操作时Piar<double>不会报错。
2、运行时类型查询只适用于原始类型。
试图查询一个对象是否属于某个泛型时,倘若使用instanceof会得到一个编译错误,如果使用强制类型转换会得到一个warning。例如:
if(a instanceof Pair<String>) //Error
if(a instanceof Pair<T>) //Error
Pair<String> p = (Pair<String>) a; //warning. 只能测试出a是一个Pair
同样的道理,getClass方法总是返回原始类型。
Pair<Sting> s = new Pair(...);
Pair<Emplcyee> e = new Pair(...);
if(s.getClass == e.getClass) //true,它们是相等的,因为都返回的是Pair.class
3、不能创建参数化类型的数组。
不能实例化参数化类型的数组,如:
Pair<String>[] table = new Pair<String>[10]; //error
因为类型擦除后,table[]的类型变成了Object[], 数组的类型是不能改变的,所以我们试图向table[]中存入其他类型的数据就会出错。
需要说明的是,只是不能用new Pair<Sring>[10]这类语句创建数组,但是可以声明它的句柄,如Pair<String>[] table;是完全合法的。
4、不能实例化类型变量。
不能使用像new T(...)、new T[...]或T.class这样的表达式中的类型变量,例如:
public Pair(){ first = new T(); } //Error
5、不能构造泛型数组。
就像上一条不能实例化类型变量一样,也不能实例化数组。
public static <T extends Comparable> T[] minmax(T[] a){ T[] mm = new T[2];} //error
类型擦除会让这个方法永远无法构造Comparable[2]数组。
6、泛型类的静态上下文中类型变量无效。
不能在静态域或方法中引用类型变量,如:
public class Sing<T>{
private static T sing; //Error
public static T getSing(){} //Error
}
7、不能抛出或捕获泛型类的实例。
既不能抛出也不能捕获泛型类的对象。实际上,甚至泛型类拓展Throwable都是不合法的。
public class Problem<T> extends Exception{/*...*/} //Error
catch子句不能使用类型变量,但在异常规范中允许使用。
public static <T> void work throws T{ //OK
try{
//...
}catch(T e){ //Error
}
}
8、可以消除对受查异常的检查。
9、注意擦除后的冲突。
下一篇:通配符类型