Java 下一代: 没有继承性的扩展(一)

了解 Groovy、Scala 和 Clojure 如何将行为融入到类中

Java 语言的设计有目的地进行了一定的删减,以避免前代产品中已发现的一些问题

。例如,Java 语言的设计人员感觉 C++ 中的多重继承性带来了太多复杂性,所以它们

选择不包含该特性。事实上,他们在该语言中很少构建扩展性选项,仅依靠单一继承和

接口。

其他语言(包括 Java 下一代语言)存在巨大的扩展潜力。在本期和接下来的两期文

章中,我将探索扩展 Java 类而不涉及继承性的途径。在本文中,您会了解如何向现有

类添加方法,无论是直接还是通过语法糖 (syntactic sugar)。

表达式问题

表达式问题是最近的计算机科学历史上的一个众所周知的观察结果,首创于贝尔实验

室的 Philip Wadler 的一篇未发表的论文。(Stuart Sierra 在其 developerWorks 文

章 “通过 Clojure 1.2 解决表达式问题” 中出色地解释了它。在这篇文章中,Wadler

说道:

表达式问题是老问题的新名字。我们的目标是通过案例定义数据类型,在这里,在不

重新编译现有代码的情况下,您可以将新的案例添加到数据类型和数据类型的新函数中

,同时保留静态类型安全(例如,没有转换)。

换句话说,您如何向一个分层结构中的类添加功能,而不求助于类型转换或 if 语句

我们将通过一个简单的例子来表明表达式问题在真实世界中的表现形式。假设您公司

始终假设应用程序中的长度单位为米,没有在您的类中为任何其他长度单位构建任何功

能。但是,有一天,您公司与一家竞争对手合并了,而这个竞争对手始终假设长度单位

为英尺。

本栏目更多精彩内容:http://www.bianceng.cn/Programming/Java/

解决该问题的一种方法是,通过使用转换方法扩展 Integer,使两种格式之间的切换

变得无关紧要。现代语言提供了多种解决方案来实现此目的;在本期中,我将重点介绍

其中的 3 种:

开放类

包装器类

协议

Groovy 的类别和 ExpandoMetaClass

Groovy 包含两种使用开放类 扩展现有的类的不同方式,“重新开放” 一个类定义

来实现更改(例如添加、更改或删除方法)的能力。

类别类

类别类(一种借鉴自 Objective-C 的概念)是包含静态方法的常规类。每个方法至

少接受一个参数,该参数表示方法扩充的类型。如果希望向 Integer 添加方法,例如我

需要接受该类型作为第一个参数的静态方法,如清单 1 所示:

清单 1. Groovy 的类别类

1
2
3
4
5
6
7
8
9
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. 使用类别类

1
2
3
4
5
6
@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 代码块的词法范围内是透明的,这

很好,因为它限制了扩充(有时是一些核心)类的范围。

时间: 2024-11-05 14:40:38

Java 下一代: 没有继承性的扩展(一)的相关文章

Java 下一代: 没有继承性的扩展(三)

Groovy 元编程为您提供常见问题的简单解决方案 Java 下一代语言扩展现有的类和其他构件的方法有很多,前两期 Java 下一代 文章探讨了其中的一些方法.在本期文章中,我将继续该探索,仔细查看在多种上下文中实现扩展的 Groovy 元编程技术. 在 "没有继承性的扩展,第 1  部分" 中,在讨论使用类别类  和 ExpandoMetaClass 作为将新行为 "应用于" 现有类的机制时,我偶然接触了一些 Groovy 元编程特性.Groovy 中的元编程特性

Java 下一代: 没有继承性的扩展(二)探索 Clojure 协议

"没有继承性的扩展,第 1 部分" 主要讨论了 Goovy.Scala 和 Clojure 中为现有类添加新方法的机制,这也是 Java 下一代语言实现无继承扩展的方法之一.本文将探讨 Clojure 的协议如何以创新的方法拓展 Java 扩展功能,为表达式问题提供出色的解决方案. 尽管这期文章主要关注可扩展性,但也会略为涉及一些允许 Clojure 和 Java 代码无缝互操作的 Clojure 特性.这两种语言有着根本性的差别(Java 是命令式.面向对象的:而 Clojure 是

Java 下一代: Groovy、Scala 和 Clojure 中的共同点(一)

探究这些下一代 JVM 语言如何处理操作符重载 编程语言中的好理念可以延续并扩展到其他语言,就像美酒一样历久弥香.因此,不足奇怪的是,Java 下一代语言 - Groovy.Scala 和 Clojure - 具有很多共同的特性.在本期和下一期 Java 下一代 文章中,我将探讨每种语言语法中功 能清单的一致性.我从能够重载操作符这个特性说起  - 克服了Java 语言中长期存在的一个缺点. 操作符重 载 如果您改造过 Java BigDecimal 类,可能看到过类似于清单 1 的代码: 清单

Java 下一代: Groovy、Scala 和 Clojure 中的共同点(二)

了解Java 下一代语言如何减少样板代码和降低复杂性 Java 编程语言诞生时所面临的限制与如今的开发人员所面临的条件有所不同.具体来讲,由于上世纪 90 年代中期的硬 件的性能和内存限制,Java 语言中存在原语类型.从那时起,Java 语言不断在演化,通过自动装箱(autobox)消除了许 多麻烦操作,而下一代语言(Groovy.Scala 和 Clojure)更进一步,消除了每种语言中的不一致性和冲突. 在这 一期的文章中,我将展示下一代语言如何消除一些常见的 Java 限制,无论是语法上

Java 下一代: Groovy、Scala 和 Clojure 中的共同点(三)

反思异常.表达式和空 在 上一期文章 中,我介绍了 Java 下一代语言用来消除 Java 语言中华而不实的东西和复杂性的创新方式.在这一期 文章中,我将展示这些语言如何消除 Java  的一些瑕疵:异常.语句与表达式,以及围绕 null 的边缘情况. 表达式 Java  语言从 C 语言那里继承的一项传承是区分编程语言 和编程表达式.Java 语句的示例包 括使用 if 或 while 的代码行,以及使用 void 来声明不会返回任何值的方法的代码行.表达式(比如 1 + 2 )用于求取 某一

对Java下的Junit的扩展

Android SDK 1.5已经将JUnit包含进来了,重新用的时候还出了一点问题,还是决定写一篇比较详细的文章,供大家和自己以后使用,写起来也挺方便的,Android下的Junit是对java下的junit的扩展,殊途同归,基本类似~ Junit简介 JUnit是 一个开源的java单元测试框架.在1997年,由 Erich Gamma 和 Kent Beck 开发完成.这两个牛人中 Erich Gamma 是 GOF 四人帮之一:Kent Beck 是 XP (Extreme Progra

介绍Java下一代语言Clojure、Scala和Groovy的共同点

在 上一期文章 中,我介绍了 Java 下一代语言用来消除 Java 语言中华而不实的东西和复杂性的创新方式.在这一期文章中,我将展示这些语言如何消除 Java 的一些瑕疵:异常.语句与表达式,以及围绕 http://www.aliyun.com/zixun/aggregation/19527.html">null 的边缘情况. 表达式 Java 语言从 C 语言那里继承的一项传承是区分编程语言 和编程表达式.Java 语句的示例包括使用 if 或 while 的代码行,以及使用 void

核心函数特性及Java下一代语言如何实现和组合它们

所有编程语言都在增加函数特性,因为运行时已变得强大到足够适应性能或内存开销.函数式编程的许多收益之一是,您可将麻烦或容易出错的任务卸载到运行时.另一个收益是将函数特性简洁地组合到您代码中的能力. 在本期文章中,我将探讨 Java 下一代语言中的内存化.然后,通过利用 Clojure 示例,我将展示通过利用函数特性之间的协调作用,如何实现http://www.aliyun.com/zixun/aggregation/17253.html">常见问题的一般解决方案. 内存化 内存化 这个词是

诊断Java代码:设计可扩展的应用程序,第1部分

随着对能应付日益增长的各种信息处理任务的软件系统需求的增长,找到能降低新的代码项目的生产成本的办法对软件公司是一种诱惑.最明显的办法之一是提高其它项目的代码的可重用程度. 在程序员设计一个新系统时,由此出现的更常见的问题中的两个是: 系统应该有多大的可扩展性? 我能使系统具有多大的可扩展性? 如果原始系统被设计成可扩展的,那么重用代码是最佳的办法.否则,重用代码时碰到的困难可以容易地抵消任何已获得的生产率.但是,要设计成可扩展的,在软件设计中就要考虑各种各样的新问题. 我将在本文讨论一些办法,这