Welcome 微信登录

首页 / 软件开发 / JAVA / 诊断Java代码: 轻松掌握Java泛型,第3部分

诊断Java代码: 轻松掌握Java泛型,第3部分2011-02-11 IBM Eric E. Allen这一系列主要讨论在 Java 编程中添加泛型类型,本文是其中的一篇,将研究还未讨论过的有关使用泛型的两个限制之一,即添加对裸类型参数的 new 操作的支持(如类 C<T> 中的 new T() )。

正如我 上个月所提到的那样,Tiger 和 JSR-14 通过使用“类型消除(type erasure)”对 Java 语言实现泛型类型。使用类型消除(type erasure),泛型类型仅用于类型检查;然后,用它们的上界替换它们。由此定义可知:消除将与如 new T() 之类的表达式冲突。

如果假定 T 的界限是 Object ,那么这一表达式将被消除为 new Object() ,并且不管对 T 如何实例化( String 、 List 、 URLClassLoader 等等), new 操作将产生一个新的 Object 实例。显然,这不是我们想要的。

要添加对表达式(如 new T() )的支持,以及添加对我们上次讨论过的其它与类型相关的操作(如数据类型转换和 instanceof 表达式)的支持,我们必须采用某种实现策略而不是类型消除(如对于每个泛型实例化,使用独立的类)。但对于 new 操作,需要处理其它问题。

尤其是,为了实现对 Java 语言添加这种支持,必须对许多基本的语言设计问题作出决定。

有效的构造函数调用

首先,为了对类型参数构造合法的 new 表达式(如 new T() ),必须确保我们调用的构造函数对于 T 的每个实例化都有效。但由于我们只知道 T 是其已声明界限的子类型,所以我们不知道 T 的某一实例化将有什么构造函数。要解决这一问题,可以用下述三种方法之一:

要求类型参数的所有实例化都包括不带参数的(zeroary)构造函数。

只要泛型类的运行时实例化没有包括所需的构造函数,就抛出异常。

修改语言的语法以包括更详尽的类型参数界限。

第 1 种方法:需要不带参数的构造函数

只要求类型参数的所有实例化都包括不带参数的构造函数。该解决方案的优点是非常简单。使用这种方法也有先例。

处理类似问题的现有 Java 技术(象 JavaBean 技术)就是通过要求一个不带参数的构造函数来解决问题的。然而,该方法的一个主要缺点是:对于许多类,没有合理的不带参数的构造函数。

例如,表示非空容器的任何类在构造函数中必然使用表示其元素的参数。包括不带参数的构造函数将迫使我们先创建实例,然后再进行本来可以在构造函数调用中完成的初始化。但该实践会导致问题的产生(您可能想要阅读 2002 年 4 月发表的本专栏文章“The Run-on Initializer bug pattern”,以获取详细信息;请参阅 参考资料。)

第 2 种方法:当缺少所需构造函数时,抛出异常

处理该问题的另一种方法是:只要泛型类的运行时实例化没有包括所需构造函数,就抛出异常。请注意:必须在运行时抛出异常。因为 Java 语言的递增式编译模型,所以我们无法静态地确定所有将在运行时发生的泛型类的实例化。例如,假设我们有如下一组泛型类:

清单 1.“裸”类型参数的 New 操作

class C<T> {
T makeT() {
return new T();
}
}
class D<S> {
C<S> makeC() {
return new C<S>();
}
}