java泛型契约_轻松掌握 Java 泛型 (第 2 部分)

轻松掌握 Java 泛型 (第 2 部分)

来源:** 收集整理

J2SE 1.5 — 代号为“

JSR-14 原型编译器中预先

型类型的基础知识,以及为

明了为 Tiger 制定的泛型

型类型的上下文。

Tiger” — 计划在 2003 年年

展示的那样,现在可下载获得)

什么它们是对 Java 语言的一个

类型的实现怎么会包含数个“缺

底发布,它将包括泛型类型(如在

。在第 1 部分中,我们讨论了泛

重要且迫切需要的补充。我们还说

陷”,这些缺陷限制了可以使用泛

为了帮助新程序员有效

Tiger 和 JSR-14 中是被禁

)为了在 JVM 上兼容地实

地使用泛型类型,我将详细说明

止的,并将说明为什么这些限制

现泛型类型所使用的实现策略的

到底泛型类型的哪些用法在

是 JSR-14(理所当然还有 Tiger

必然结果。

泛型类型的限制

让我们先查阅一下 Tiger 和 JSR-14 中泛型类型的使用限制:

不应在静态成员中引用封闭类型参数。

不能用基本类型实例化泛型类型参数。

不能在数据类型转换或 instanceof 操作中使用“外露”类型参数。

不能在 new 操作中使用“外露”类型参数。

不能在类定义的 imple

ments 或 extends 子句中使用

“外露”类型参数。

为什么会有这些限制呢?这要归因于

的机制。由于 JVM 根本不支持泛型类型

泛型类型的支持 — 它们用泛型类型信息

生成只包含普通类型的类文件。

Tiger 和 JSR-14 为在 JVM 上实现泛型类型所使用

,所以这些编译器“耍了个花招”,使得似乎存在对

检查所有的代码,但随即“擦除”所有的泛型类型并

例如,将象 List< T> 这样的泛型类型擦除得只剩下 List。“外露”类型参数 — 单独出现而不是位于某个类型中的类型参数(如类 List< T> 中的类型参数 T)— 被简单地擦除成它们的上界(就 T 而言,其上界就是 Object)。

这一技术的功能极其强大;我们可以使几乎所有泛型类型的精度得到增强,但又与 JVM 保持兼容。事实上,我们甚至可以交替地使用非泛型的旧类(比如 List)和其对应的泛型类(List< T>);两者在运行时看起来是一样的。

遗憾的是,正如以上的限制所示,获

系统中引入了缺陷,这些缺陷限制我们使

得这一功能是有代价的。以这种方式进行擦除在类型

用泛型类型的安全性。

为了帮助说明每种限制

限制。与后两个限制有关的

,我们查阅会出现这些限制的示

问题过于复杂,因而需要更深入

例。在本文中,我们将讨论前三个

的研究,留待下一篇文章讨论。

静态成员中的封闭类型参数

编译器完全禁止在静态方法和静态内

码在 Tiger 中就是非法的:

部类中引用封闭类型参数。所以,举例来说,以下代

清单 1. 在静态上下文中非法引用封闭类型参数 class C< T> {

static void m() {

T t;

}

static class D {

C< T> t;

}

}

当编译这一代码时,会生成两个错误:

在静态方法 m 中非法引用 T 的错误

在静态类 D 中非法引用 T 的错误

当定义静态字段时,情

中共享该类中的静态字段。

明中引用类型参数,编译器

时导致奇怪的错误,如在不

况变得更加复杂。在 JSR-14 和

现在,在 JSR-14 编译器 1.0

不会报错,但它本应该这么做。

包含数据类型转换的代码中出现

Tiger 中,在泛型类的所有实例

和 1.2 中,如果您在静态字段声

字段被共享这一事实很容易在运行

ClassCastException。

例如,以下程序将在这两个版本的 JSR-14 下通过编译而没有任何警告:

清单 2. 在静态字段中对封闭类型参数的有问题的引用 class C< T> {

static T member;

C(T t) { member = t; }

T getMember() { return member; }

public static void

main(String[] args) {

C< String> c = new C< String>("test");

System.out.print

ln(c.getMember().toString())

;

new C< Integer>(new Integer(1));

System.out.println(c.getMemb

er().toString());

}

}

请注意,每次分配类 C 的实例时,都要重新设置静态字段 member。而且,它被设置成的对象类型取决于 C 的实例的类型!在所提供的 main 方法中,第一个实例 c 是 C< String> 类型。而第二个是 C< Integer> 类型。每当从 c 访问 member 这一共享静态字段时,总是假定 member 的类型是 String。但是,在分配了类型为 C< Integer> 的第二个实例之后,member 的类型是 Integer。

运行 C 的 main 方法的结果可能会

源代码根本没有包含任何数据类型转换,

据类型转换插入到代码中,这样做是为了

实。这些数据类型转换被期望能够成功,

让您吃惊 — 它将发出一个 ClassCastException!

怎么会这样呢?事实证明编译器确实在编译阶段将数

解决类型擦除会降低某些表达式的类型的精度这一事

但在本例中却没有成功。

应该认为 JSR-14 1.0

性,或者可以说,它破坏了

做的那样,只要防止程序员

和 1.2 的这一特殊“功能”是

类型系统应该和程序员达成的“

在静态字段中引用泛型类型,情

个错误。它破坏了类型系统的健全

基本契约”。象对静态方法和类所

况就会好很多。

请注意允许这种有潜在

码中覆盖类型系统。问题是

操作,错误地在字段声明中

“爆炸性”的代码存在所带来的

程序员可能会无意中编写这样的

包括静态修饰符)。

问题并不是程序员有意在自己的代

代码(比如,由于“复制和粘贴”

类型检查器应该能帮助程序员从这些

实际上会使程序员更迷惑。当未使用数据

ClassCastException 时,我们应如何诊

的实现方案而又恰好假定类型系统合理运

类型系统不是合理地运行。

类型的错误中恢复,但对于静态字段而言,类型系统

类型转换的代码中显示的唯一错误就是

断这样的错误?对于不清楚 Tiger 中泛型类型所用

行的程序员而言,情况更糟。因为在这样的情况下,

幸运的是,JSR-14 的

此,我们有理由期待在 Tig

最新版本(1.3)宣布在静态字

er 的静态字段中使用类型参数

段中使用类型参数是不合法的。因

也是不合法的。

泛型类型参数和基本类型

和我们刚才讨论的不同,这一限制没有同样的潜在缺陷,但它会使您的代码非常冗长。例如,在 java.util.Hashtable 的泛型版本中,有两种类型参数:用于 Key 类型的和用于 Value 类型的。因此,如果我们想要一个将 String 映射到 String 的 Hashtable,我们可以用表达式 new Hashtable< String, String>() 指定新的实例。但是,如果我们想要一个将 String 映射到 int 的 Hashtable,我们只能创建 Hashtable< String, Integer> 的实例,并将所有的 int 值包装在 Integer 中。

同样,Tiger 在这方面

界限,而界限不能是基本类

当然也是由所用的实现方案得到

型,所以一旦类型被擦除,则对

的。既然类型参数被擦除为它们的

基本类型的实例化会完全没有意义

数据类型转换或 instanceof 操作中的“外露”参数

回想一下,对于“外露”类型参数,我们是指在词汇上单独出现的类型参数,而不是更大类型的语法子组件。例如,C< T> 不是“外露”类型参数,但(在 C 主体中)T 是。

如果在代码中对“外露”类型参数进

出名为“unchecked”的警告。例如,以

type T:

行数据类型转换或 instanceof 操作,则编译器将发

下代码将生成警告:Warning: unchecked cast to

清单 3. 带 unchecked

操作的泛型代码 import java.

util.Hashtable;

interface Registry {

public void register(Object o);

}

class C< T> implements Registry {

int counter = 0;

Hashtable< Integer, T> values;

public C() {

values = new Hashtable< Integer, T>();

}

public void register(Object o) {

values.put(new Integer(count

er), (T)o);

counter++;

}

}

您应该严肃地对待这些警告,因为它们说明您的代码在运行时会表现得非常奇怪。事实上,它们会使得诊断代码变得极为困难。在以前的代码中,我们认为如果对实例 C< JFrame> 调用 register("test"),会发出 ClassCastException。但并非如此;计算将继续,就仿佛数据类型转换成功了一样,然后在进一步进行计算时发出错误,或者更糟:用遭破坏的数据完成计算,但不向外发出任何错误信号。同样,对“外露”类型参数的 instanceof 检查将在编译时产生“unchecked”警告,而且检查将不会如期在运行时进行。

双刃剑

那么,这里到底发生了

instanceof 测试中的外露

Object)。因此,对类型

什么?因为 Tiger 依靠类型擦

类型参数被“擦除”为它们的上

参数的数据类型转换将变成对参

除,所以数据类型转换和

界(在前面的例子中,那将是类型

数上界的转换。

同样,instanceof 将

做的,如果是的话,我们完

数据类型转换和 instanceo

检查操作数是否是参数界限的 i

全可以显式地强制转换为界限。

f 检查。

nstanceof。那根本不是我们打算

因此,通常应避免对类型参数使用

然而,有时为了编译代码,您必须依

只要记住,在代码的那一部分中,类型检

靠对类型参数的数据类型转换。如果是这样的情况,

查不保险 — 要靠自己。

尽管泛型类型是制作健

而且极难诊断和修正。下次

型 Java 类型系统中包括它

壮代码的强大武器,但我们已经

,我们将介绍 Tiger 中泛型类

们时必定会出现的一些问题。

发表于:2004.12.24 16:49

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值