Java 语言的设计有目的地进行了一定的删减,以避免前代产品中已发现的一些问题。例如,Java 语言的 设计人员感觉 C++ 中的多重继承性带来了太多复杂性,所以它们选择不包含该特性。事实上,他们在该语言 中很少构建扩展性选项,仅依靠单一继承和接口。
其他语言(包括 Java 下一代语言)存在巨大的扩 展潜力。在本期和接下来的两期文章中,我将探索扩展 Java 类而不涉及继承性的途径。在本文中,您会了解 如何向现有类添加方法,无论是直接还是通过语法糖 (syntactic sugar)。
表达式问题
表达式 问题是最近的计算机科学历史上的一个众所周知的观察结果,首创于贝尔实验室的 Philip Wadler 的一篇未 发表的论文。(Stuart Sierra 在其 developerWorks 文章 “通过 Clojure 1.2 解决表达式问题” 中出色 地解释了它。在这篇文章中,Wadler 说道:
表达式问题是老问题的新名字。我们的目标是 通过案例定义数据类型,在这里,在不重新编译现有代码的情况下,您可以将新的案例添加到数据类型和数据 类型的新函数中,同时保留静态类型安全(例如,没有转换)。
换句话说,您如何向一个分 层结构中的类添加功能,而不求助于类型转换或 if 语句?
我们将通过一个简单的例子来表明表达式 问题在真实世界中的表现形式。假设您公司始终假设应用程序中的长度单位为米,没有在您的类中为任何其他 长度单位构建任何功能。但是,有一天,您公司与一家竞争对手合并了,而这个竞争对手始终假设长度单位为 英尺。
解决该问题的一种方法是,通过使用转换方法扩展 Integer,使两种格式之间的切换变得无关 紧要。现代语言提供了多种解决方案来实现此目的;在本期中,我将重点介绍其中的 3 种:
开放类
包装器类
协议
Groovy 的类别和 ExpandoMetaClass
Groovy 包含两种使用开放类 扩展现有的类的不同方式,“重新开放” 一个类定义 来实现更改(例如添加、更改或删除方法)的能力。
类别类
类别类(一种借鉴自 Objective-C 的概念)是包含静态方法的常规类。每个方法至少接受一个参数,该参数表示方法扩充的类型。如果希望向 Integer 添加方法,例如我需要接受该类型作为第一个参数的静态方法,如清单 1 所示:
清单 1. Groovy 的类别类
class IntegerConv { static Double getAsMeters(Integer self) { self * 0.30480 } static Double getAsFeet(Integer self) { self * 3.2808 } }
清单 1 中的 IntegerConv 类包含两个扩充方法,每个扩充方法都接受一个名为 self(一个通用 的惯用名称)的 Integer 参数。要使用这些方法,我必须将引用代码包装在一个 use 代码块中,如清单 2 所示:
清单 2. 使用类别类
@Test void test_conversion_with_category() { use(IntegerConv) { assertEquals(1 * 3.2808, 1.asFeet, 0.1) assertEquals(1 * 0.30480, 1.asMeters, 0.1) } }
清单 2 中有两个特别有趣的地方。首先,尽管 清单 1 中的扩展方法名为 getAsMeters(),但我 将它称为 1.asMeters。Groovy 围绕 Java 中的属性的语法糖使我能够执行 getAsMeters() 方法,好像它是 名为 asMeters 的类的一个字段一样。如果我在扩展方法中省略了 as,对扩展方法的调用需要使用空括号, 就像 1.asMeters() 中一样。一般而言,我喜欢更干净的属性语法,这是编写特定于域的语言 (DSL) 的一种 常见技巧。
清单 2 中第二个需要注意的地方是对 asFeet 和 asMeters 的调用。在 use 代码块中, 我同等地调用新方法和内置方法。该扩展在 use 代码块的词法范围内是透明的,这很好,因为它限制了扩充 (有时是一些核心)类的范围。
ExpandoMetaClass
类别是 Groovy 添加的第一种扩展机制。但 事实证明对构建 Grails(基于 Groovy 的 Web 框架)而言,Groovy 的词法范围限制太多了。由于不满类别 中的限制,Grails 的创建者之一 Graeme Rocher 向 Groovy 添加了另一种扩展机制:ExpandoMetaClass。
ExpandoMetaClass 是一种懒惰实例化的扩展持有者,它可从任何类 “成长” 而来。清单 3 展示了 如何使用 ExpandoMetaClass,为我的 Integer 类实现我的扩展:
清单 3. 使用 ExpandoMetaClass 扩展 Integer
class IntegerConvTest{ static { Integer.metaClass.getAsM { -> delegate * 0.30480 } Integer.metaClass.getAsFt { -> delegate * 3.2808 } } @Test void conversion_with_expando() { assertTrue 1.asM == 0.30480 assertTrue 1.asFt == 3.2808 } }
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索groovy
, 方法
, java代码求助
, 清单
, clojure
, integer
, 表达式
, conversation扩展消息
一个
scala groovy clojure、groovy clojure、clojure scala、clojure和scala、clojure 还是 scala,以便于您获取更多的相关知识。