【java基础(五十五)】泛型的约束与局限(二)

不能构造泛型数组

就想不能实例化一个泛型实例一样,也不能实例化数组。不过原因有所不同,毕竟数组会填充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) {
		....
	}
}

捐赠

若你感觉读到这篇文章对你有启发,能引起你的思考。请不要吝啬你的钱包,你的任何打赏或者捐赠都是对我莫大的鼓励。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章