因为java虚拟机没有泛型这一说法,所以在使用java泛型时需要考虑一些限制,大多数限制都是由类型擦除引起的。
1.不能用基本类型实例化类型参数
不能用类型参数代替基本类型。因此,没有Pair<double>,只有Pair<Double>。当然其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。
2.运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。例如:
Pair<String> pair = new Pair<String>(); if(pair instanceof Pair<String>) //error if(pair instanceof Pair<T>) //error if(pair instanceof Pair) //true
那么同样的道理,getClass方法总是返回原始类型。例如:
Pair<String> a = new Pair<String>(); Pair<Integer> b = new Pair<Integer>(); System.out.println(a.getClass().equals(b.getClass())); //输出true
3.不能创建参数化类型数组
不能实例化参数化类型的数组,例如:
Pair<String> pair = new Pair<String>[10]; //error
这有什么问题呢?擦除之后,pair的类型就变成了Pair[],这明显和我们的本意有区别,我们的本意是想根据不同的类型参数创建不同的表,所以java泛型直接限制不能创建。
4.不能实例化类型变量
不能使用想new T(...),new T[...]或T.class这样的表达式中的类型变量,例如,下面的Pair<T>构造器就是非法的:
public Pair(){first = new T();} //error
类型擦除将T改变成Object,而且我们本意肯定不希望调用new Object()。但是,可以通过反射调用Class.newInstance方法来构造泛型对象(在操作数据库时一般用这种方法封装不同的实体)。例如:
public static <T> Pair<T> makePair(Class<T> c1){ try { return new Pair<>(c1.newInstance(),c1.newInstance()); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); return null; } }
同理,不能创建一个泛型数组:
public static <T> void minmax(T[] t){ T[] mm = new T[2]; //error }
类型擦除会让这个方法永远构造Object[2]数组,同样我们可以通过反射调用Array.newInstance方法来构造泛型数组。(这样创建在什么地方用我还不知道,以前没用过)
public static <T> void minmax(T[] t){ T[] mm = (T[]) Array.newInstance(t.getClass().getComponentType(), t.length); }
5.泛型类的静态上下文中类型变量无效
public class Problem<T>{ private static T first; //error public static T getFirst(){} //error }
6.注意擦除后的冲突
当泛型类型被擦除时,无法创建引发冲突的条件。下面是一个示例:
public class pair<T>{ public void equals(T value); }
这个方法泛型擦除后变成public void equals(Object value),这个方法与Object.equals方法冲突。补救的方法只能重新命名。
7.不能抛出或捕获泛型类的实例
既不能抛出也不能捕获泛型类对象。实际上,甚至泛型类扩展Throwable都是不合法的,例如以下定义就不能正常编译:
public class Problem<T> extends Exception{} //error
catch子句不能使用类型变量,例如:以下方法将不能编译
public static <T extends Throwable> void doWork(){ try { } catch (T e) { //error } }
不过在异常规范中使用类型变量是允许的,以下方法是合法的:
public static <T extends Throwable> void doWork(T t) throws T{ try { } catch (Exception e) { throw t; } }
参考资料:java核心技术 卷一