ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:34.20KB ,
资源ID:5383566      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5383566.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(java中得泛型.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

java中得泛型.docx

1、java中得泛型 诊断 Java 代码: 轻松掌握 Java 泛型2008年09月04日 社区交流关键字: DbUnit 单元测试过程 持续集成 Maven XQuery 动态编译 内容摘要:Eric Allen 提供了代码样本,这些样本通过重点描述诸如基本类型的限制、受限泛型和多态方法之类的 Tiger 特性来说明泛型类型的优缺点(即将发表的专栏文章将讨论其它特性,比如 Tiger 中泛型类型的特定表现以及可能扩展为 Tiger 之外的泛型类型)。 J2SE 1.5 代号为 Tiger 计划在 2003 年年底发布。我一直都热衷于尽可能多地收集有关即将推出的新技术的预告信息,因此我将撰写一系

2、列的文章,讨论可从 V1.5 中获得的新的和经过重组的特性,本文是第一篇。我特别想谈谈泛型类型并重点讲述在 Tiger 中为了支持它们而进行的更改和调整。在许多方面,Tiger 肯定是迄今为止在 Java 编程方面(包括对源语言语法的重大扩展)所取得的最大进步。Tiger 中计划进行的最显著的变化是添加泛型类型,正如在 JSR-14 原型编译器中所预先展示的那样(您可以立即免费下载该编译器;请参阅 参考资料)。 让我们从介绍泛型类型是什么以及添加了什么特性来支持它们开始吧。数据类型转换和错误为理解泛型类型为何如此有用,我们要将注意力转向 Java 语言中最容易引发错误的因素之一 需要不断地将表

3、达式向下类型转换(downcast)为比其静态类型更为具体的数据类型(请参阅 参考资料中的“The Double Descent bug pattern”,以了解进行数据类型转换时,可能会碰到的麻烦的某些方面)。 程序中的每个向下类型转换对于 ClassCastException 而言都是潜在的危险,应当尽量避免它们。但是在 Java 语言中它们通常是无法避免的,即便在设计优良的程序中也是如此。 在 Java 语言中进行向下类型转换最常见的原因在于,经常以专用的方式来使用类,这限制了方法调用所返回的参数可能的运行时类型。例如,假定往 Hashtable 中添加元素并从中检索元素。那么在给定的程

4、序中,被用作键的元素类型和存储在散列表中的值类型,将不能是任意对象。通常,所有的键都是某一特定类型的实例。同样地,存储的值将共同具有比 Object 更具体的公共类型。 诊断 Java 代码: 轻松掌握 Java 泛型2008年09月04日 社区交流关键字: DbUnit 单元测试过程 持续集成 Maven XQuery 动态编译 内容摘要:Eric Allen 提供了代码样本,这些样本通过重点描述诸如基本类型的限制、受限泛型和多态方法之类的 Tiger 特性来说明泛型类型的优缺点(即将发表的专栏文章将讨论其它特性,比如 Tiger 中泛型类型的特定表现以及可能扩展为 Tiger 之外的泛型类

5、型)。 但是在目前现有的 Java 语言版本中,不可能将散列表的特定键和元素声明为比 Object 更具体的类型。在散列表上执行插入和检索操作的类型特征符告诉我们只能插入和删除任意对象。例如, put 和 get 操作的说明如下所示: 清单 1. 插入检索类型说明表明只能是任意对象class Hashtable Object put(Object key, Object value) .Object get(Object key) .因此,当我们从类 Hashtable 的实例检索元素时,比如,即使我们知道在 Hashtable 中只放了 String ,而类型系统也只知道所检索的值是 Obj

6、ect 类型。在对检索到的值进行任何特定于 String 的操作之前,必须将它强制转换为 String ,即使是将检索到的元素添加到同一代码块中,也是如此! 清单 2. 将检索到的值强制转换成 Stringimport java.util.Hashtable;class Test public static void main(String args) Hashtable h = new Hashtable();h.put(new Integer(0), value);String s = (String)h.get(new Integer(0);System.out.println(s);请

7、注意 main 方法主体部分的第三行中需要进行的数据类型转换。因为 Java 类型系统相当薄弱,因此代码会因象上面那样的数据类型转换而漏洞百出。这些数据类型转换不仅使 Java 代码变得更加拖沓冗长,而且它们还降低了静态类型检查的价值(因为每个数据类型转换都是一个选择忽略静态类型检查的伪指令)。我们该如何扩展该类型系统,从而不必回避它呢?用泛型类型来解决问题!要消除如上所述的数据类型转换,有一种普遍的方法,就是用 泛型类型来增大 Java 类型系统。可以将泛型类型看作是类型“函数”;它们通过类型变量进行参数化,这些类型变量可以根据上下文用各种类型参数进行 实例化。例如,与简单地定义类 Hash

8、table 不同,我们可以定义泛型类 Hashtable ,其中 Key 和 Value 是类型参数。除了类名后跟着尖括号括起来的一系列类型参数声明之外,在 Tiger 中定义这样的泛型类的语法和用于定义普通类的语法很相似。例如,可以按照如下所示的那样定义自己的泛型 Hashtable 类: 清单 3. 定义泛型 Hashtable 类class Hashtable . 然后可以引用这些类型参数,就像我们在类定义主体内引用普通类型那样,如下所示:清单 4. 像引用普通类型那样引用类型参数class Hashtable .Value put(Key k, Value v) .Value get(

9、Key k) .类型参数的作用域就是相应类定义的主体部分(除了静态成员之外)(在下一篇文章中,我们将讨论为何 Tiger 实现中有这样的“怪习”,即必须对静态成员进行此项限制。请留意!)。创建一个新的 Hashtable 实例时,必须传递类型参数以指定 Key 和 Value 的类型。传递类型参数的方式取决于我们打算如何使用 Hashtable 。在上面的示例中,我们真正想要做的是创建 Hashtable 实例,它只将 Integer 映射为 String 。可以用新的 Hashtable 类来完成这件事: 清单 5. 创建将 Integer 映射为 String 的实例import java

10、.util.Hashtable;class Test public static void main(String args) Hashtable h = new Hashtable();h.put(new Integer(0), value);.现在不再需要数据类型转换了。请注意用来实例化泛型类 Hashtable 的语法。就像泛型类的类型参数用尖括号括起来那样,泛型类型应用程序的参数也是用尖括号括起来的。 清单 6. 除去不必要的数据类型转换.String s = h.get(key);System.out.println(s);当然,程序员若只是为了能使用泛型类型而必须重新定义所有的标准

11、实用程序类(比如 Hashtable 和 List )的话,则可能会是一项浩大的工程。幸好,Tiger 为用户提供了所有 Java 集合类的泛型版本,因此我们不必自己动手来重新定义它们了。此外,这些类能与旧代码和新的泛型代码一起无缝工作(下个月,我们会说明如何做到这一点)。Tiger 的基本类型限制Tiger 中类型变量的限制之一就是,它们必须用引用类型进行实例化 基本类型不起作用。因此,在上面这个示例中,无法完成创建从 int 映射到 String 的 Hashtable 。 这很遗憾,因为这意味着只要您想把基本类型用作泛型类型的参数,您就必须把它们组装为对象。另一方面,当前的这种情况是最糟

12、的;您不能将 int 作为键传递给 Hashtable ,因为所有的键都必须是 Object 类型。 我们真正想看到的是,基本类型可以自动进行包装(boxing)和解包装(unboxing),类似于用 C# 所进行的操作(或者比后者更好)。遗憾的是,Tiger 不打算包括基本类型的自动包装(但是人们可以一直期待 Java 1.6 中出现该功能!)。受限泛型有时我们想限制可能出现的泛型类的类型实例化。在上面这个示例中,类 Hashtable 的类型参数可以用我们想用的任何类型参数进行实例化,但是对于其它某些类,我们或许想将可能的类型参数集限定为给定类型 范围内的子类型。例如,我们可能想定义泛型

13、ScrollPane 类,它引用普通的带有滚动条功能的 Pane 。被包含的 Pane 的运行时类型通常会是类 Pane 的子类型,但是静态类型就只是 Pane 。 有时我们想用 getter 检索被包含的 Pane ,但是希望 getter 的返回类型尽可能具体些。我们可能想将类型参数 MyPane 添加到 ScrollPane 中,该类型参数可以用 Pane 的任何子类进行实例化。然后可以用这种形式的子句: extends Bound 来说明 MyPane 的声明,从而来设定 MyPane 的范围: 清单 7. 用 extends 子句来说明 MyPane 声明class ScrollPa

14、ne . 当然,我们可以完全不使用显式的范围,只要能确保没有用不适当的类型来实例化类型参数。为什么要自找麻烦在类型参数上设定范围呢?这里有两个原因。首先,范围使我们增加了静态类型检查功能。有了静态类型检查,就能保证泛型类型的每次实例化都符合所设定的范围。其次,因为我们知道类型参数的每次实例化都是这个范围之内的子类,所以可以放心地调用类型参数实例出现在这个范围之内的任何方法。如果没有对参数设定显式的范围,那么缺省情况下范围是 Object ,这意味着我们不能调用范围实例在 Object 中未曾出现的任何方法。多态方法除了用类型参数对类进行参数化之外,用类型参数对方法进行参数化往往也同样很有用。泛

15、型 Java 编程用语中,用类型进行参数化的方法被称为 多态方法(Polymorphic method)。 多态方法之所以有用,是因为有时候,在一些我们想执行的操作中,参数与返回值之间的类型相关性原本就是泛型的,但是这个泛型性质不依赖于任何类级的类型信息,而且对于各个方法调用都不相同。例如,假定想将 factory 方法添加到 List 类中。这个静态方法只带一个参数,也将是 List 唯一的元素(直到添加了其它元素)。因为我们希望 List 成为其所包含的元素类型的泛型,所以希望静态 factory 方法带有类型变量 T 这一参数并返回 List 的实例。 但是我们确实希望该类型变量 T 能

16、在方法级别上进行声明,因为它会随每次单独的方法调用而发生改变(而且,正如我在下一篇文章中将讨论的那样,Tiger 设计的“怪习”规定静态成员不在类级类型参数的范畴之内)。Tiger 让我们通过将类型参数作为方法声明的前缀,从而在单独的方法级别上声明类型参数。例如,可以按照如下所示的那样为 factory 方法 make 添加前缀: 清单 8. 将类型参数作为前缀添加到方法声明class Utilities public static List make(T first) return new List(first); 除了多态方法中所增加的灵活性之外,Tiger 中还增加了一个优点。Tiger

17、 使用类型推断机制,根据参数类型来自动推断出多态方法的类型。这可以大大减少方法调用的繁琐和复杂性。例如,如果想调用 make 方法来构造包含 new Integer(0) 的 List 新实例,那么只需编写: 清单 9. 强制 make 构造新实例Utilities.make(Integer(0)然后会自动地从方法参数中推断出类型参数的实例化。结束语正如我们所见到的那样,在 Java 语言中添加泛型类型肯定会大大增强我们使用静态类型系统的能力。学习如何使用泛型类型相当简单,但是同样也需要避免一些缺陷。在接下来的文章中,我们将讨论如何充分使用将出现在 Tiger 中的泛型类型的特定表现,以及一些

18、缺陷。我们还将研究对泛型 Java 类型工具的扩展,我们期盼这些工具可以出现在仍处于设计阶段的 Java 平台之中。J2SE 1.5 代号为“Tiger” 计划在 2003 年年底发布,它将包括泛型类型(如在 JSR-14 原型编译器中预先展示的那样,现在可下载获得)。在 第 1 部分中,我们讨论了泛型类型的基础知识,以及为什么它们是对 Java 语言的一个重要且迫切需要的补充。我们还说明了为 Tiger 制定的泛型类型的实现怎么会包含数个“缺陷”,这些缺陷限制了可以使用泛型类型的上下文。 为了帮助新程序员有效地使用泛型类型,我将详细说明到底泛型类型的哪些用法在 Tiger 和 JSR-14

19、中是被禁止的,并将说明为什么这些限制是 JSR-14(理所当然还有 Tiger)为了在 JVM 上兼容地实现泛型类型所使用的实现策略的必然结果。泛型类型的限制让我们先查阅一下 Tiger 和 JSR-14 中泛型类型的使用限制:不应在静态成员中引用封闭类型参数。 不能用基本类型实例化泛型类型参数。 不能在数据类型转换或 instanceof 操作中使用“外露”类型参数。 不能在 new 操作中使用“外露”类型参数。 不能在类定义的 implements 或 extends 子句中使用“外露”类型参数。 为什么会有这些限制呢?这要归因于 Tiger 和 JSR-14 为在 JVM 上实现泛型类型

20、所使用的机制。由于 JVM 根本不支持泛型类型,所以这些编译器“耍了个花招”,使得似乎存在对泛型类型的支持 它们用泛型类型信息检查所有的代码,但随即“擦除”所有的泛型类型并生成只包含普通类型的类文件。例如,将象 List 这样的泛型类型擦除得只剩下 List 。“外露”类型参数 单独出现而不是位于某个类型中的类型参数(如类 List 中的类型参数 T ) 被简单地擦除成它们的上界(就 T 而言,其上界就是 Object )。 强大;我们可以使几乎所有泛型类型的精度得到增强,但又与 JVM 保持兼容。事实上,我们甚至可以交替地使用非泛型的旧类(比如 List )和其对应的泛型类( List );

21、两者在运行时看起来是一样的。 遗憾的是,正如以上的限制所示,获得这一功能是有代价的。以这种方式进行擦除在类型系统中引入了缺陷,这些缺陷限制我们使用泛型类型的安全性。为了帮助说明每种限制,我们查阅会出现这些限制的示例。在本文中,我们将讨论前三个限制。与后两个限制有关的问题过于复杂,因而需要更深入的研究,留待下一篇文章讨论。静态成员中的封闭类型参数编译器完全禁止在静态方法和静态内部类中引用封闭类型参数。所以,举例来说,以下代码在 Tiger 中就是非法的:清单 1. 在静态上下文中非法引用封闭类型参数class C static void m() T t;static class D C t;当编

22、译这一代码时,会生成两个错误:在静态方法 m 中非法引用 T 的错误 在静态类 D 中非法引用 T 的错误 当定义静态字段时,情况变得更加复杂。在 JSR-14 和 Tiger 中,在泛型类的所有实例中共享该类中的静态字段。现在,在 JSR-14 编译器 1.0 和 1.2 中,如果您在静态字段声明中引用类型参数,编译器不会报错,但它本应该这么做。字段被共享这一事实很容易在运行时导致奇怪的错误,如在不包含数据类型转换的代码中出现 ClassCastException 。 例如,以下程序将在这两个版本的 JSR-14 下通过编译而没有任何警告:清单 2. 在静态字段中对封闭类型参数的有问题的引用

23、内容摘要:这个月,Eric Allen 继续讨论 JSR-14 和 Tiger 中的泛型类型(generic type)。他概括了在这些 Java 扩展中强制实施的几个限制,并说明这些扩展语言的编译器所使用的实现策略如何使这些限制必然存在。 class C static T member;C(T t) member = t; T getMember() return member; public static void main(String args) C c = new C(test);System.out.println(c.getMember().toString();new C(ne

24、w Integer(1);System.out.println(c.getMember().toString();请注意,每次分配类 C 的实例时,都要重新设置静态字段 member 。而且,它被设置成的对象类型取决于 C 的实例的类型!在所提供的 main 方法中,第一个实例 c 是 C 类型。而第二个是 C 类型。每当从 c 访问 member 这一共享静态字段时,总是假定 member 的类型是 String 。但是,在分配了类型为 C 的第二个实例之后, member 的类型是 Integer 。 运行 C 的 main 方法的结果可能会让您吃惊 它将发出一个 ClassCastExc

25、eption !源代码根本没有包含任何数据类型转换,怎么会这样呢?事实证明编译器确实在编译阶段将数据类型转换插入到代码中,这样做是为了解决类型擦除会降低某些表达式的类型的精度这一事实。这些数据类型转换 被期望能够成功,但在本例中却没有成功。 应该认为 JSR-14 1.0 和 1.2 的这一特殊“功能”是个错误。它破坏了类型系统的健全性,或者可以说,它破坏了类型系统应该和程序员达成的“基本契约”。象对静态方法和类所做的那样,只要防止程序员在静态字段中引用泛型类型,情况就会好很多。请注意允许这种有潜在“爆炸性”的代码存在所带来的问题并不是程序员 有意在自己的代码中覆盖类型系统。问题是程序员可能会

26、无意中编写这样的代码(比如,由于“复制和粘贴”操作,错误地在字段声明中包括静态修饰符)。 类型检查器应该能帮助程序员从这些类型的错误中恢复,但对于静态字段而言,类型系统实际上会使程序员更迷惑。当未使用数据类型转换的代码中显示的唯一错误就是 ClassCastException 时,我们应如何诊断这样的错误?对于不清楚 Tiger 中泛型类型所用的实现方案而又恰好假定类型系统合理运行的程序员而言,情况更糟。因为在这样的情况下,类型系统不是合理地运行。 幸运的是,JSR-14 的最新版本(1.3)宣布在静态字段中使用类型参数是不合法的。因此,我们有理由期待在 Tiger 的静态字段中使用类型参数也

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

28、由所用的实现方案得到的。既然类型参数被擦除为它们的界限,而界限不能是基本类型,所以一旦类型被擦除,则对基本类型的实例化会完全没有意义。数据类型转换或 instanceof 操作中的“外露”参数回想一下,对于“外露”类型参数,我们是指在词汇上单独出现的类型参数,而不是更大类型的语法子组件。例如, C 不是“外露”类型参数,但(在 C 主体中) T 是。 如果在代码中对“外露”类型参数进行数据类型转换或 instanceof 操作,则编译器将发出名为“unchecked”的警告。例如,以下代码将生成警告: Warning: unchecked cast to type T :清单 3. 带 unc

29、hecked 操作的泛型代码import java.util.Hashtable;interface Registry public void register(Object o);class C implements Registry int counter = 0;Hashtable values;public C() values = new Hashtable();public void register(Object o) values.put(new Integer(counter), (T)o);counter+;您应该严肃地对待这些警告,因为它们说明您的代码在运行时会表现得非常

30、奇怪。事实上,它们会使得诊断代码变得极为困难。在以前的代码中,我们认为如果对实例 C 调用 register(test) ,会发出 ClassCastException 。但并非如此;计算将继续,就仿佛数据类型转换成功了一样,然后在进一步进行计算时发出错误,或者更糟:用遭破坏的数据完成计算,但不向外发出任何错误信号。同样,对“外露”类型参数的 instanceof 检查将在编译时产生“unchecked”警告,而且检查将不会如期在运行时进行。双刃剑那么,这里到底发生了什么?因为 Tiger 依靠类型擦除,所以数据类型转换和 instanceof 测试中的外露类型参数被“擦除”为它们的上界(在前面的例子中

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1