Welcome 微信登录

首页 / 软件开发 / JAVA / Java 下一代: 没有继承性的扩展(三)

Java 下一代: 没有继承性的扩展(三)2014-10-22 IBM Neal FordGroovy 元编程为您提供常见问题的简单解决方案

Java 下一代语言扩展现有的类和其他构件的方法有很多,前两期 Java 下一代 文章探讨了其中的一些方法。在本期文章中,我将继续该探索,仔细查看在多种上下文中实现扩展的 Groovy 元编程技术。

在 “没有继承性的扩展,第 1  部分” 中,在讨论使用类别类  和 ExpandoMetaClass 作为将新行为 “应用于” 现有类的机制时,我偶然接触了一些Groovy 元编程特性。Groovy 中的元编程特性更深入一些:它们使得集成 Java 代码变得更容易,而且可以帮助您采用比 Java 语言更简洁的方式来执行常见任务。

接口强制转换(Interface coercion)

接口是 Java 语言中常见的语义重用机制。尝试以简洁的方式集成 Java 代码的其他语言应该提供简单的方法来具体化接口。在 Groovy 中,类可以通过传统的 Java方式来扩展接口。但是,Groovy 还使得在方便时轻松地将闭包和映射强制转换成接口实例变得很容易。

单一方法强制转换

清单 1 中的 Java 代码示例使用 FilenameFilter 接口来定位文件:

清单 1. 在 Java 中使用    FilenameFilter接口列出文件

import java.io.File;import java.io.FilenameFilter;public class ListDirectories {public String[] listDirectoryNames(String root) {return new File(root).list(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {return new File(name).isDirectory();}});}}
在 清单 1 中,我创建了一个新的匿名内部类,它覆盖了指定过滤条件的 accept() 方法。在Groovy 中,我可以跳过创建一个新类的步骤,只将一个闭包强制转换成接口,如清单 2 所示:

清单 2. 在 Groovy 中通过使用闭包强制转换来模拟    FilenameFilter接口

new File(".").list({ File dir, String name -> new File(name).isDirectory() } as FilenameFilter).each { println it }
在 清单 2 中,list() 方法想使用一个 FilenameFilter实例作为参数。但我却创建了一个与接口的 accept() 签名相匹配的闭包,并在闭包的正文中实现接口的功能。在定义了闭包之后,我通过调用 as  FilenameFilter 将闭包强制转换成适当的 FilenameFilter 实例。Groovy 的 as运算符将闭包具体化为一个实现接口的类。该技术对于单一方法接口非常适用,因为方法和闭包之间存在一个自然映射。

对于指定多个方法的接口,被具体化的类为每个方法都调用了相同的闭包块。但只在极少数情况下,用相同代码来处理所有方法调用才是合理的。当您需要使用多个方法时,可以使用包含方法名称/闭包对的  Map,而不是使用单一的闭包。

映射

在 Groovy 中,还可以使用映射来表示接口。映射的键是代表方法名称的字符串,键值是实现方法行为的代码块。清单 3 中的示例将一个映射具体化为一个  Iterator 实例:

清单 3. 在 Groovy中使用映射来具体化接口

h = [hasNext:{ h.i > 0 }, next:{h.i--}]h.i = 10def iterator = h as Iteratorwhile (iterator.hasNext())print iterator.next() + ", "// 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
在 清单 3 中,我创建了一个映射 (h),它包括 hasNext 和  next 键,以及它们各自的代码块。Groovy 假设映射键是字符串,所以我不需要用引号来包围该键。在每个代码块中,我用点符号  (h.i) 引用 h 映射的第三个键 (i)。这个点符号借鉴自人们所熟悉的对象语法,它是 Groovy中的另一个语法糖示例。在使用 h 作为一个迭代器之前,不会执行代码块,我必须首先确保 i 有一个值,然后再使用  h 作为一个迭代器。我用 h.i = 10 设置 i 的值。然后,我将 h选作一个 Iterator,并使用从 10 开始的整数集合。

通过使得映射能够动态地作为接口实例,Groovy 极大地减少了 Java 语言有时导致的一些语法问题。此特性很好地说明了 Java 下一代语言如何改进开发人员的体验。

ExpandoMetaClass

正如我在 "没有继承性的扩展,第  1 部分" 中所述,您可以使用 ExpandoMetaClass 将新方法添加到类 — 包括核心类,比如  Object 和 String。ExpandoMetaClass对于其他一些用途也是有用的,比如将方法添加到对象实例,以及改善异常处理。