Unmi 学习 Groovy 之闭包

一. 认识闭包

将代码块作为方法参数进行传递,这种机制就叫做闭包。闭包可以引用在创建闭包的范围中可见的变量。最近关于闭包的讨论也比较多,闭包能使语言更具灵动性,在动态脚本语言中较广泛的支持,如 Perl、Python、Ruby、JavaScript,还有我们的 Groovy。

有些语言能把函数作为参数传递,如 JavaScript 的回调函数,Python,甚至是 C++ 的函数指针。而 Java 在这方面又略逊一筹,需搬动一个匿名的内部类来实现类似的功能,内部类只能访问外部声明为 final 的变量。不过有呼声要在 Java SE 7 中增加闭包特性,让我们试目以待吧。

Groovy 这回大概是从 Ruby 那儿偷得闭包的语法。前面说这么多,其实你看到了就会发现,其实闭包很简单的,不信,请看:

用大括号括起来的,给它一个名字 logo 的那段就是一个闭包(有点像 Java 中的语句块);闭包可以通过执行它的 call() 方法调用,或者直接把它当作一个常规方法对待。闭包也可以没有名字,比如下面要讲的集合方法中用的闭包。

二. 闭包的参数

闭包可以有参数。如果是一个参数的话,该参数就直接映射到名为 it 的变量,如:

如果是多个参数的闭包,则在闭包中用 "->" 把参数列表和实现隔开,如:(当然一个参数也可以这么方式定义的)

我看到这里,怎么越来越觉得闭包那么像 C/C++ 中的宏定义呢?

调用闭包时,如果参数数量不对会抛出 IncorrectClosureArgumentException;不过对于一个参数的闭包,可以少传但不能多传参数,不传参数时,闭包认为 it 为 null。

三. 闭包的传递

闭包在实现上是扩展自 groovy.lang.Closure 类,因为它们是类,所以可以作为参数传递给其他的方法,下面就来看一个例子:

输出为:

Action occured: Status changed
Object saved to the database: Status changed

上面这种用法可用在事件触发、回调操作或策略模式中。

四. 闭包与变量作用域

在 Groovy 中闭包可以访问创建它的上下文中定义的变量,可在闭包中修改;而且闭包内部定义的变量在周围的上下文中也是可见的。看如下代码片断:

执行的结果是:

0.3
0.2

五. 闭包与集合操作

当闭包与集合整合时,它们展现了真正的威力。Groovy 中的 List、Map、String、和 Range 都有接受闭包参数的额外方法,譬如字符串也是字符的集合,也可以这么用。

涉及到的集合操作有 each、collect、inject、find、findAll、every、any。下面以例子来帮助理解这些方法的使用。


注意:上面的闭包传给方法,如果是在 GroovyShell 中逐行敲入代码时,起始花括号"{" 必须与调用方法在同一行上,比如说写成下面的方式就有问题:

然而如果你想要在单独一行上指定起始花括号,可以使用下语法(调用方法后加个圆括号):

要是写在 .groovy 文件中或是在 GroovyConsole 中不受这个限制,但是为规范和不致换个环境又出错,还是应该让起起始花括号"{" 与调用方法在同一行。

其实怎么去理解这种要求呢?主要是两点:

其一是为什么我们通常不写这个圆括呢?那是 Groovy 允许方法调用时省略圆括号

还有就是在 GroovyShell 下,如果输完方法名,如 each,然后马上回车,就报错

ERROR groovy.lang.MissingPropertyException: Exception evaluating property 'each' for java.util.ArrayList, Reason: groovy
.lang.MissingPropertyException: No such property: each for class: java.lang.Integer
at groovysh_evaluate.run (groovysh_evaluate:1)
...

只有方法名后面加个圆括号,它才知道是参数列表开始,直至匹配的右圆括号结束,或是加了花括,它也知道是一个闭包的开始,直到匹配的花括号结束。


1) collect() 方法利用指定的闭包转换集合的元素

2) inject() 方法,可将前一次的迭代的结果传递给下一次迭代,请看例子:

输出为:

Previous: 0 - Current: 1
Previous: 1 - Current: 2
Previous: 2 - Current: 3

在当前迭代中能知道上一次所迭代的值。inject 接受两个参数,第一次迭代中使用的注入值,和将要使用的闭包。这个闭包也必须定义两个参数,分别为上次迭代的注入值和当前的元素。注意在闭包中必须返回下次迭代中注入的值。否则,就会假设为 null。

为了现好的理解发这个注入语法,我们将上述例子分开来写成如下:

3) find() 方法找到符合闭包中所定义条件的第一次出现的元素,找不到则返回 null

4) findAll() 方法是返回所有符合闭包中所定义条件的元素所组成的集合

5) every() 方法检查集合中是否每一个元素都符合闭包中指定的条件,是则返回 true,否则为 false

6) any() 方法则检查集合中是否有一个元素符合闭包中指定的条件,有则返回 true,否则为 false

参考:1. 《Java 脚本编程语言、框架与模式》第 4 章

本文链接 https://yanbin.blog/unmi-study-groovy-closure/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments