因为Java并没有真正实现泛型,是编译器在编译的时候在字节码上做了手脚,这种实现理念造成Java泛型本身有很多漏洞。为了规避这些问题Java对泛型的使用上做了一些约束,但不可避免的还是有一些问题存在。这其中大多数限制都是由类型擦除引起的。
1.不能用类型参数替换基本类型:
2.运行时类型检查只是针对泛型类的原始类型进行的。getClass()方法只返回原始类型,因为JVM根本就不知道泛型这回事,它只知道原始类型。
3.异常。不能抛出也不能捕获泛型类的对象,使用泛型类来拓展Throwable也是非法的。
4.不能声明参数化类型的数组。原因还是类型擦除。
Pair<String>[] p = new Pair<String>[100];//这个是非法的,不能创建泛型数组
如果这样做合法的话,就会引发一个严重问题:
类型擦除之后,p的类型为Pair[],将其转换为Object[]是合法的
Object[] o = p;
数组可以记住它的元素类型,如果试图存入一个错误类型的元素,就会抛出一个ArrayStoreException异常,但是
o[0] = new Pair<Date>();
是可以通过数组存储的检测,因为经过类型擦除Pair<Date>和Pair<String>是相同的类型,但是在取数据的时候,比如o[0].getFirst(),我们知道编译器在字节码中加入了类型强制转换的指令,即(String)o[0].getFirst(),这时候类型错误就会发生,但这之前的一系列操作看起来又都是合法的。Java语言的严密性受到挑战。(如果参数化类型数组像列子那样使用,会导致运行时类型不匹配的错误,而在java中这种错误应该在编译期发现,所以java禁止使用参数化类型数组)
5.泛型类型的实例化。
不能实例化泛型类型。例如,下面Pair<T>构造器是非法的:
public Pair(){ first = new T(); second = new T(); }
类型擦除将变量T替换成Object,但这段代码的本意肯定不是调用new Object()。
类似地,不能建立一个泛型数组:
public <T> T[] minmax(T[] a){ T[] mm = new T[2]; ... }
类型擦除会让这个方法总是构造一个Object[2]数组。
但是,通过调用Class.newInstance和Array.newInstance方法,可以利用反射构造泛型对象和数组。
6.静态上下文。不能在静态域或者静态方法中引用类型变量。见如下代码:
class C<T> { static T member; C(T t) { member = t; } T getMember() { return member; } public static void main(String[] args) {C<String> c = new C<String>("test");//1 System.out.println(c.getMember().toString()); new C<Integer>(new Integer(1));//2 System.out.println(c.getMember().toString()); } }
静态域和静态方法是共享的。C中的静态域member到底是什么类型的哪?对程序而言这是个潜在的安全隐患,因为在执行语句2的时候会发生
ClassCastException异常,因为编译器在获取域的时候加入了强制类型转换,不能要求原来是String类型的域,现在引用一个Integer对象。
7.类型擦除后的冲突。例如:
public class B<T>{
}
这两个方法实际上是冲突的,类型擦除后它们具有相同的签名。补救的办法只能是重新命名。