https://www.ibm.com/developerworks/cn/java/j-djc05133/
developerWorks 中国 > Java technology >
诊断 Java 代码: 轻松掌握 Java 泛型,第 4 部分泛型类型如何能管教惹是生非的 mixin |
级别: 初级 Eric E. Allen (eallen@cs.rice.edu), 博士研究生, Java 编程语言团队,Rice 大学 2003 年 7 月 21 日 Java 开发人员和研究人员 Eric Allen 讨论了通过泛型类型添加对 mixin 支持所带来的影响,并以此文来结束他对 JSR-14 和 Tiger 中泛型类型的由四部分组成的讨论。(您也可以单击本文顶部或底部的“讨论”来访问该论坛。) 至此,在这个讨论 JSR-14 和 Tiger 中泛型类型的微型系列中,我们已经探讨了:
本月,我们将探讨在可以处理 mixin(可能被期望是泛型类型中最强大的功能)之前需要先解决的问题,以此来结束对 Java 语言中泛型类型的讨论。 mixin 是由其父类参数化的类。例如,请考虑以下这个泛型类,它继承了它本身的类型参数:
类 包装不仅需要添加访问被包装类的功能的转发方法,而且它还阻止我们在需要被包装对象的实例的上下文中使用由此产生的可滚动对象(例如,我们不能将 在上面的示例中,我们甚至可以对类型参数施加约束以阻止它用于不适当的上下文中。例如,我们可能想使该类型参数强制为
那么我们的 mixin 只能继承 GUI 组件。
通常,mixin 作为独立语言功能部件添加到某种语言中,就象 Jam 中的那样。但是合并 mixin 以作为泛型类型系统的一部分很吸引人,几乎可以说魅力无穷。原因是:mixin 和泛型类都能被认为是将现有类映射到新类的 函数。 泛型类可被视为将它们的参数映射成新实例化的函数。mixin 可被视为将现有类映射成新子类的函数。通过使用泛型类型合并 mixin,我们能解决其它 mixin 公式的许多关键限制。 在 Java 语言的 Jam 扩展中,mixin 的超类类型没有名称;我们就不能在 mixin 主体中引用它。这一限制会迅速引起一连串各种其它问题。例如,在 Jam 中,禁止程序员将 请考虑访问者模式,其中用 将 mixin 明确表述为泛型类,我们就始终有父类的句柄,它是该类继承的类型参数。例如,我们可以将 但是,将 mixin 明确表述为泛型类型时有其它一些明显的困难。为了让您初步体会可能产生的某些困难,我们将讨论几个突出的困难以及一些可能的解决方案。
在讨论任何其它问题之前,我们应该先指出,与上月讨论的泛型类型的功能扩展一样,通过使用由 JSR-14 和 Tiger 使用的简单 类型消除(type erasure)策略,不能将对 mixin 的支持添加到 Java 语言中。 要了解其原因,请考虑在继承类型参数的类被消除时会出现什么情况。该类会最终继承类型参数的 界限!例如,上一个示例中类 为了通过泛型类型支持 mixin,我们 需要获得泛型类型实例化的运行时表示。幸运的是,编码这一信息的方法有许多,它们实际上都向后与 Tiger 兼容。这样的向后兼容编码方案是泛型 Java(Generic Java)的 NextGen 公式的显著特点(在 参考资料一节中)。
在我们希望允许类继承类型参数时立即出现的紧迫问题是要决定我们能调用什么样的超级构造函数?请回忆:每个 Java 类构造函数都必须调用超类的构造函数。通常,通过查找超类并确保存在匹配的超级构造函数,类型检查器确保这些超级构造函数调用会成功。 但是在我们对超类所知的一切只限于它是类型参数的实例化时,对于什么样的构造函数可用于给定的实例化,我们没有任何概念。而且请注意,类型检查器甚至不能检查是否每个 mixin 实例化都会产生有效的超级构造函数调用。其原因是:在某些其它上下文中,mixin 的参数可能用类型参数界限实例化了。 例如,泛型类 解决这一问题的各种方案与我们上月 第 3 部分中讨论的针对检查
就如 第三种解决方案可能有点繁琐,但是它有许多优点。注释类型参数,其中包括所有实例化都必须拥有的构造函数集。这些注释确切地告知我们针对类型参数,我们可以可靠地调用什么样的构造函数。因此,当类型参数
任何 mixin 公式都会产生一个非常严重的问题:特定 mixin 的方法名可能与其超类的潜在实例化的方法名冲突。例如,假设类 产生的类显示如下: 清单 1. 意外方法覆盖的示例
随后 mixin 实例化 当 mixin 被明确表述为泛型类时,意外覆盖的问题特别讨厌。因为 mixin 的父类可能用类型参数被实例化,因此类型检查器就不能确定意外方法覆盖的所有情况。而且,在意外覆盖出现时抛出运行时异常是无法接受的,因为客户机程序员无法预测何时将抛出这样的异常。如果我们想编写可靠的程序,那么我们必须禁止在运行时出现无法预料的错误。 另一个解决方案是只隐藏这些相互冲突的方法中的一个,并解析所有匹配的方法调用以引用未隐藏的方法。这个解决方案的问题是我们希望诸如 在 1998 年召开的有关编程语言原理的 ACM SIGPLAN-SIGACT 研讨会(请参阅 参考资料)上,Felleisen、Flatt 和 Krishnamurthi 提出了在 mixin 不属于泛型类型的上下文中针对该问题的一个好的解决方案:基于使用 mixin 实例化的上下文来解决对相互冲突的方法的引用。在这个解决方案中,mixin 包含有这样的观点:确定在名称不一致的情况中要调用哪个方法。 在 mixin 作为泛型类型的情况中,我们可以应用同样的解决方案。我们只要设计一些 观点,这些观点在泛型类型的上下文中有效,并且还允许向后兼容 JVM。在 Rice JavaPLT 实验室中,我们已经在“A First-Class Approach to Genericity”(请参阅 参考资料)一文中提出了这样一种解决方案。
正如示例、问题和可能的解决方案所演示的,在 Java 编程中继承泛型类型以包含对 mixin 的支持会产生一种功能强大的语言,但同时也引入了一些有待克服的问题。这是典型的编程语言设计:只能通过使许多现有功能变复杂才能添加所希望的功能。在编程语言领域中,没有任何免费的午餐。
|