现在,闭包是 Java 世界的一个重大主题,对于是否会在 Java 7 中包含闭包仍然存在热烈的争论。有些人会问:既然 Groovy 中已经存在闭包,为什么 Java 语言中还需要闭包?这一节将学习 Groovy 中的闭包。如果没有意外,在闭包成为 Java 语法的正式部分之后,这里学到的内容将给您带来方便。
虽然在前几节编写了不少集合代码,但还没有实际地在集合上迭代。当然,您知道 Groovy 就是 Java,所以如果愿意,那么总是能够得到 Java 的 Iterator
实例,用它在集合上迭代,就像下面这样:
def acoll = ["Groovy", "Java", "Ruby"] |
实际上在 for
循环中并不需要类型声明,因为 Groovy 已经将迭代转变为任何集合的直接成员。在这个示例中,不必获取 Iterator
实例并直接操纵它,可以直接在集合上迭代。而且,通常放在循环构造内的行为(例如 for
循环体中 println
)接下来要放在闭包内。在深入之前,先看看如何执行这步操作。
对于上面的代码,可以用更简洁的方式对集合进行迭代,如下所示:
def acoll = ["Groovy", "Java", "Ruby"] |
请注意,each
直接在 acoll
实例内调用,而 acoll
实例的类型是 ArrayList
。在 each
调用之后,引入了一种新的语法 — {
,然后是一些代码,然后是 }
。由 {}
包围起来的代码块就是闭包。
闭包是可执行的代码块。它们不需要名称,可以在定义之后执行。所以,在上面的示例中,包含输出 it
(后面将简单解释 it
)的行为的无名闭包将会在 acoll
集合类型中的每个值上被调用。
在较高层面上,{}
中的代码会执行三次,从而生成如图 13 所示的输出。
图 13. 迭代从未像现在这样容易
闭包中的 it
变量是一个关键字,指向被调用的外部集合的每个值 — 它是默认值,可以用传递给闭包的参数覆盖它。下面的代码执行同样的操作,但使用自己的项变量:
def acoll = ["Groovy", "Java", "Ruby"] |
在这个示例中,用 value
代替了 Groovy 的默认 it
。
闭包在 Groovy 中频繁出现,但是,通常用于在一系列值上迭代的时候。请记住,一系列值可以用多种方式表示,不仅可以用列表表示 — 例如,可以在映射、String
、JDBC Rowset
、File
的行上迭代,等等。
如果想在前面一节 “Groovy 中的映射” 中的 hash
对象上迭代,可以编写以下代码:
def hash = [name:"Andy", "VPN-#":45] |
请注意,闭包还允许使用多个参数 — 在这个示例中,上面的代码包含两个参数(key
和 value
)。
以下是使用典型的 Java 构造如何进行同样的迭代:
Map<String, String>map = new HashMap<String, String>(); |
上面的代码比 Groovy 的代码长得多,是不是?如果要处理大量集合,那么显然用 Groovy 处理会更方便。
请记住,凡是集合或一系列的内容,都可以使用下面这样的代码进行迭代。
"ITERATION".each{ |
虽然在迭代上使用闭包的机会最多,但闭包确实还有其他用途。因为闭包是一个代码块,所以能够作为参数进行传递(Groovy 中的函数或方法不能这样做)。闭包在调用的时候才会执行这一事实(不是在定义的时候)使得它们在某些场合上特别有用。
例如,通过 Eclipse 创建一个 ClosureExample
对象,并保持它提供的默认类语法。在生成的 main()
方法中,添加以下代码:
def excite = { word -> |
这段代码是名为 excite
的闭包。这个闭包接受一个参数(名为 word
),返回的 String
是 word
变量加两个感叹号。请注意在 String
实例中替换 的用法。在 String
中使用 ${value}
语法将告诉 Groovy 替换 String
中的某个变量的值。可以将这个语法当成 return word + "!!"
的快捷方式。
既然有了闭包,下面就该实际使用它了。可以通过两种方法调用闭包:直接调用或者通过 call()
方法调用。
继续使用 ClosureExample
类,在闭包定义下面添加以下两行代码:
assert "Groovy!!" == excite("Groovy") |
可以看到,两种调用方式都能工作,但是直接调用的方法更简洁。不要忘记闭包在 Groovy 中也是一类对象 — 既可以作为参数传递,也可以放在以后执行。用普通的 Java 代码可以复制同样的行为,但是不太容易。现在不会感到惊讶了吧?