泛型编程(四)

本文探讨了Java泛型编程的一些局限性,包括类型参数不能为原始类型、运行时类型检查的限制、无法抛出或捕获泛型异常、泛型类对象无法构建数组、初始化类型变量的困难以及在静态方法中使用类型变量的禁止。这些限制主要源于类型擦除,导致在某些场景下可能导致类型安全性降低或编码不便。
摘要由CSDN通过智能技术生成

泛型编程的局限性

下面介绍泛型编程的局限性,他们中大多数都是因为类型擦除引起的。

类型参数不能为原始类型

你无法使用原始类型初始化类型参数,比如,你没有办法使用Pair<double>,只有Pair<Double>。究其原因,是因为类型擦除之后,泛型类型被Object或者边界类型代替,但是double并不是从Object继承而来。
这一点通常非常难受,但并不致命,因为你可以使用包装类。

运行时类型检查只对类型擦除后的类有效

Java虚拟机内部没有泛型类,因此当你在运行时进行类型检查时,指挥检查泛型类,而不会检查泛型类型。比如

if(a instanceof Pair<String>) // 和a instanceof Pair是一样的

只会检查a是不是Pair类型,下面的测试也是一样

if(a instanceof Pair<T>) // T被忽略

为了让你避免发生类型转换的错误,每次你对泛型类使用instanceof或者类型转换时,编译器会给出警告。
由于同样的原因,getClass函数会返回原始类型。比如

Pair<String> stringPair = ...;
Pair<Employee> employeePair = ...;
if(stringPair.getClass() == employeePair.getClass()) // 相等

最后的判断会返回true,因为他们都返回Pair.Class。

没有办法抛出泛型类对象

你没有办法抛出或捕获泛型类对象。事实上,你没有办法让泛型类继承自Throwable。比如,下面的定义会导致编译错误。

public class Problem<T> extends Throwable{/*...*/} // 错误,不能继承自Throwable。

在catch语句中,你也不能使用类型变量

public static <T extends Throwable> void doWork(Class<T> t)
{
	try{
		doWork
	}
	catch(T e){
		Logger.global.info(...);
	}
}

但是,在catch语句内部,你可以使用泛型变量。比如下面的方法是合法的

public static <T extends Throwable> void doWork(T t) throws T // OK
{
	try
	{
		dowork
	}
	catch(Throwable realCause){
		t.initCause(reslCause);
		throw r;
	}
}

泛型类对象没有办法构建数组

你不能定义泛型类对象的数组

	Pair<String>[] table = new Pair<String>[10]; // 错误

有什么问题呢?类型擦除之后,table的类型是Pair[]类型,你可以将它转换成为Object[]类型。

Object[] objectArray = table;

当向数组中插入错误的数据类型时,编译器会报错,比如

objectArray[0] = "Hello"; // 错误,数组类型是Pair

但是类型擦除之后,类型变量不起作用,所以下面的语句也是合法的

objectArray[0] = new Pair<Employee>();

为了避免这个错误,编译器不允许定义泛型类数组。

不能使用初始化类型变量

你不能使用类型变量定义对象,下面的代码是不合法的

public Pair(){ first = new T(); second = new T(); } // 报错

类型擦除之后,T将会被替代为Object,你不会想调用new Object();
你可能会想到使用反射机制定义变量。但是这也很复杂,你不能使用下面的语句

first = T.class.newInstance(); // 报错

表达式T.class是不合法的,相反,你需要设计API,处理Class对象。比如

public static <T> Pair<T> makePair(Class<T> cl)
{
	try
	{
		return new Pair<T>(cl.newInstance(),cl.newInstance())
	}
	catch (Exception e)
	{
		return null;
	}
}

上面的方法应该这样调用

Pair<String> p = Pair.makePair(String.class);

Class对象本身也是泛型的,比如,String.class是Class<String>的一个对象。所以makePair函数可以推断他创建的对象的类型。

在静态函数中使用类型变量是不合法的

在静态方法中不允许使用类型变量。

public class Singleton<T>
{
	public static T getSingleInstance()  // 错误
	{
		if(singleInstance == null )...
	}
	private static T singleInstance; // Error
}

如果上面的代码合法,那么进过类型擦除后,只会有一个Singleton类型,即使设置不同的T,他们也指向同一个singleInstance。这会导致很多问题。

了解类型擦除后发生的冲突

通常,你不能实现一个泛型类,如果他集成自一个接口和一个类,而他们继承自同样的接口或类。这会导致类型擦除之后过渡函数的生成发生冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值