`

JSR-14 原型编译器中泛型类型的一些限制

阅读更多

J2SE 1.5 ― 代号为“Tiger” ― 计划在 2003 年年底发布,它将包括泛型类型(如在 JSR-14 原型编译器中预先展示的那样,现在可下载获得)。在 第 1 部分中,我们讨论了泛型类型的基础知识,以及为什么它们是对 Java 语言的一个重要且迫切需要的补充。我们还说明了为 Tiger 制定的泛型类型的实现怎么会包含数个“缺陷”,这些缺陷限制了可以使用泛型类型的上下文。

为了帮助新程序员有效地使用泛型类型,我将详细说明到底泛型类型的哪些用法在 Tiger 和 JSR-14 中是被禁止的,并将说明为什么这些限制是 JSR-14(理所当然还有 Tiger)为了在 JVM 上兼容地实现泛型类型所使用的实现策略的必然结果。

泛型类型的限制

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

    * 不应在静态成员中引用封闭类型参数。
    * 不能用基本类型实例化泛型类型参数。
    * 不能在数据类型转换或 instanceof 操作中使用“外露”类型参数。
    * 不能在 new 操作中使用“外露”类型参数。
    * 不能在类定义的 implements 或 extends 子句中使用“外露”类型参数。

为什么会有这些限制呢?这要归因于 Tiger 和 JSR-14 为在 JVM 上实现泛型类型所使用的机制。由于 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 和 Tiger 中,在泛型类的所有实例中共享该类中的静态字段。现在,在 JSR-14 编译器 1.0 和 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.println(c.getMember().toString());
    new C<Integer>(new Integer(1));
    System.out.println(c.getMember().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 的最新版本(1.3)宣布在静态字段中使用类型参数是不合法的。因此,我们有理由期待在 Tiger 的静态字段中使用类型参数也是不合法的。

泛型类型参数和基本类型

和我们刚才讨论的不同,这一限制没有同样的潜在缺陷,但它会使您的代码非常冗长。例如,在 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 是。

如果在代码中对“外露”类型参数进行数据类型转换或 instanceof 操作,则编译器将发出名为“unchecked”的警告。例如,以下代码将生成警告: Warning: unchecked cast to type T :

清单 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(counter), (T)o);
    counter++;
  }
}


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




双刃剑

那么,这里到底发生了什么?因为 Tiger 依靠类型擦除,所以数据类型转换和 instanceof 测试中的外露类型参数被“擦除”为它们的上界(在前面的例子中,那将是类型 Object )。因此,对类型参数的数据类型转换将变成对参数上界的转换。

同样, instanceof 将检查操作数是否是参数界限的 instanceof 。那根本不是我们打算做的,如果是的话,我们完全可以显式地强制转换为界限。因此,通常应避免对类型参数使用数据类型转换和 instanceof 检查。

然而,有时为了编译代码,您必须依靠对类型参数的数据类型转换。如果是这样的情况,只要记住,在代码的那一部分中,类型检查不保险 ― 要靠自己。

尽管泛型类型是制作健壮代码的强大武器,但我们已经演示了误用它们会使代码不再健壮而且极难诊断和修正。下次,我们将介绍 Tiger 中泛型类型的后两个限制,并讨论试图在泛型 Java 类型系统中包括它们时必定会出现的一些问题。

 

 

 

转自http://www.ibm.com/developerworks/cn/java/j-djc03113/

分享到:
评论

相关推荐

    undertow-websockets-jsr-2.1.7.Final-API文档-中文版.zip

    赠送jar包:undertow-websockets-jsr-2.1.7.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.1.7.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.1.7.Final-sources.jar; 赠送Maven依赖信息...

    undertow-websockets-jsr-2.1.7.Final-API文档-中英对照版.zip

    赠送jar包:undertow-websockets-jsr-2.1.7.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.1.7.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.1.7.Final-sources.jar; 赠送Maven依赖信息...

    JSR-303接口标准和参考实现

    `hibernate-validator-4.2.0.Final.jar`是这个特定版本的Hibernate Validator的归档文件,包含了所有必要的类和库,使开发者能够在他们的项目中实现JSR-303验证。 `validation-api-1.0.0.GA.jar`是JSR-303验证API的...

    jsr-275-1.0-beta-2.jar

    java.lang.ClassNotFoundException: javax.measure.converter.ConversionException所需的jar

    undertow-websockets-jsr-2.2.14.Final-API文档-中文版.zip

    赠送jar包:undertow-websockets-jsr-2.2.14.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.2.14.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.2.14.Final-sources.jar; 赠送Maven依赖信息...

    undertow-websockets-jsr-2.2.14.Final-API文档-中英对照版.zip

    赠送jar包:undertow-websockets-jsr-2.2.14.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.2.14.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.2.14.Final-sources.jar; 赠送Maven依赖信息...

    23 Spring Core参数校验之JSR303_JSR-349注解-慕课专栏1

    在软件开发中,参数校验是一项关键任务,用于确保输入数据的合法性和程序的稳定性。在前后端分离的架构中,前端通常会进行初步的参数校验,但后端校验同样重要,以防止恶意攻击和数据异常。Spring Core提供了一种...

    jsr311-api-1.1.1-API文档-中文版.zip

    赠送jar包:jsr311-api-1.1.1.jar; 赠送原API文档:jsr311-api-1.1.1-javadoc.jar; 赠送源代码:jsr311-api-1.1.1-sources.jar; 赠送Maven依赖信息文件:jsr311-api-1.1.1.pom; 包含翻译后的API文档:jsr311-api...

    JSR-75-135-172_Supported List.xls

    JSR-75-135-172_Supported List 主流手机对JSR 75 135 172的支持!

    JSR-133-JMM.zip

    本文是JSR-133规范,即JavaTM内存模型与线程规范,由JSR-133专家组开发。本规范是JSR-176(定义了JavaTM平台 Tiger(5.0)发布版的主要特性)的一部分。本规范的标准内容将合并到JavaTM语言规范、JavaTM虚拟机规范...

    JSR-168 中文版,实现门户必备。

    JSR-168的出现极大地推动了企业级门户应用的发展,尤其是在Java生态系统中。 portlet是一种轻量级的Web组件,它能够嵌入到门户页面中,提供个性化的信息展示和服务。portlet可以是静态内容、动态数据或者复杂的Web...

    jackson-datatype-jsr310-2.12.5-API文档-中文版.zip

    赠送jar包:jackson-datatype-jsr310-2.12.5.jar; 赠送原API文档:jackson-datatype-jsr310-2.12.5-javadoc.jar; 赠送源代码:jackson-datatype-jsr310-2.12.5-sources.jar; 赠送Maven依赖信息文件:jackson-...

    spring3零配置注解实现Bean定义(包括JSR-250、JSR-330)

    - **JSR-330**:又称为Dependency Injection for Java (DI4J),提供了`@Inject`注解用于依赖注入,这与Spring的`@Autowired`类似,但在标准Java库中可用。 通过这些注解,可以在类上直接标记,而无需显式地在XML...

    JSR-133-Java Memory Model Specification 中文版+英文版+参考资料

    JSR133-memory_model-1_0-pfd2-spec.pdf JSR133中文版.pdf j-jtp02244-Java.theory...The JSR-133 Cookbook.pdf jsr-133-faq.pdf JSR-133-Appr-The Java Memory Model.pdf 深入理解Java内存模型之系列篇 - CSDN博客.pdf

    JSR-168 Portlet指南.doc

    【JSR-168 Portlet开发指南】 JSR-168,全称为Java Specification Request 168,是Java社区制定的一项标准,旨在为portlet开发者提供一套API,以实现portlet的可移植性和互操作性。Portlet是一种组件化的应用程序,...

    jsr305-3.0.2-API文档-中英对照版.zip

    赠送jar包:jsr305-3.0.2.jar; 赠送原API文档:jsr305-3.0.2-javadoc.jar; 赠送源代码:jsr305-3.0.2-sources.jar; 赠送Maven依赖信息文件:jsr305-3.0.2.pom; 包含翻译后的API文档:jsr305-3.0.2-javadoc-API...

    JSR-133-ZH.pdf

    《JSR-133:JavaTM内存模型与线程规范》是Java多线程编程领域的重要指导文档,由JSR-133专家组制定,旨在解决Java平台中并发编程时可能出现的问题,确保多线程环境下的正确性和可预测性。这份规范与JavaTM平台Tiger...

    JSR-94的API,最终版的

    JSR-94(Java Specification Request 94)是Java平台企业版(Java EE)中的一个规范,它定义了一个标准接口,使得开发者能够将规则引擎集成到Java应用程序中。这个API允许应用程序与规则引擎进行交互,执行业务规则...

    JSR-107 标准手册 Specification

    JSR-107的制定工作已持续多年,尽管它因其长时间未能完成而名声不佳,但它最终将被包含在Java EE 7规范中,并有望满足日益增长的缓存需求。 在分布式计算的背景下,缓存是指存储在计算机系统中,用于快速访问的数据...

    手机定位(JSR-179 官方文档).pdf

    ### 手机定位(JSR-179)官方文档知识点详解 #### 一、文档概述与背景 在深入了解文档内容之前,我们先来了解一下这份文档的背景及其重要性。《手机定位(JSR-179)官方文档》是诺基亚论坛发布的一份关于Java ME平台上...

Global site tag (gtag.js) - Google Analytics