不能构造泛型数组
就想不能实例化一个泛型实例一样,也不能实例化数组。不过原因有所不同,毕竟数组会填充null
值,构造时看上去是安全的。不过,数组本身也有类型,用来监控存储在虚拟机中的数组。这个类型会被擦除。如:
public static <T extends Comparable> T[] minmax(T[] a) {
T[] mm = new T[2];
}
类型擦除会让这个方法永远构造Comparable[2]
数组。
如果数组仅仅作为一个类的私有实例域,就可以将这个数组声明为Object[]
,并且在获取元素时进行类型转换。如:
public class ArrayList<E> {
private Object[] elements;
@SuppressWarinings("unchecked")
public E get(int n) {return (E)elements[n];}
public void set(int n, E e) {elements[n] = e;}
}
实际的实现没有这么清晰:
public class ArrayList<E> {
private E[] elements;
...
public ArrayList() {
elements = (E[])new Object[10];
}
}
这里,强制类型转换E[]
是一个假象,而类型擦除使其无法察觉。
由于minmax
方法返回T[]
数组,使得这一技术无法施展,如果掩盖这个类型会有运行时错误结果。如:
public static <T extends Comparable> T[] minmax(T... a) {
Object[] mm = new Object[2];
...
return (T[])mm;
}
调用
String[] ss = ArrayAlg.minmax("Tom", "Dick", "Harry");
编译时不会有任何警告。当Object[]
引用赋给Comparable[]
变量时,将会发生ClassCastException
异常。
泛型类的静态上下文中类型变量无效
不能在静态域或方法中引用变量类型。如:
public class Singleton<T> {
private static T singleInstance; // Error
private static T getSingleInstance() { // Error
if (singleInstance == null)
construct new instance of T;
return singleInstance;
}
}
如果这个程序能够运行,就可以声明一个Singleton<Random>
共享随机数生成器,声明一个Singleton<JFileChooser>
共享文件选择器对话框。但是,这个程序无法工作。类型擦除之后,只剩下Singleton
类,它只包含一个singleInstance
域。因此,禁止使用带有类型变量的静态域和方法。
不能抛出或捕获泛型类的实例
既不能抛出也不能捕获泛型类对象。实际上,甚至泛型类扩展Throwable
都是不合法的。例如,以下定义就不能正常编译:
public class Problem<T> extends Exception { // Error,不能继承Throwable
...
}
catch
子句中不能使用类型变量,如,以下方法将不能编译:
public static <T extends Throwable> void doWork(Class<T> t) {
try {
...
} catch (T e) { // Error,不能在catch子句中使用类型变量
...
}
}
不过,在异常规范中使用类型变量是允许的,以下方法是合法的:
public static <T extends Throwable> void doWork(T t) throws T {
try {
...
} catch (Throwable realCause) {
....
}
}
捐赠
若你感觉读到这篇文章对你有启发,能引起你的思考。请不要吝啬你的钱包,你的任何打赏或者捐赠都是对我莫大的鼓励。