当在 Java 中定义用于注册事件处理器的方法参数或者创建短小的胶水代码时,会创建匿名内部类。该特性看上去是一个不错的想法,但是没过多久,开发者们就发现,它们变得非常冗长,尤其是对那种确实非常短的单方法接口的实现。Groovy 中的闭包就是去掉了那种冗长感的短小的匿名方法。
闭包是轻量级的,短小、简介。而且将会是在 Groovy 中使用最多的特性之一。过去传递匿名类实例的地方,现在可以传递闭包。
闭包是从函数式编程的 Lambda 表达式派生而来的。闭包是 Groovy 最强大的特性之一,而且语法上非常优雅。闭包不是简单地替代匿名方法,它还可以变成解决有效高内存需求问题的一种通用工具。
举个栗子:
def sum(n) {
total = 0
for (int i = 2; i <= n; i += 2) {
total += i
}
total
}
println "Sum of even numbers for 1 to 100 is ${sum(100)}"
def product(n) {
prod = 1
for (int i = 2; i <= n; i += 2) {
prod = prod * i
}
prod
}
println "Product of even numbers for 1 to 100 is ${product(10)}"
def sqr(n) {
squared = []
for (int i = 2; i <= n; i += 2) {
squared << i
}
squared
}
println "Squares of even numbers for 1 to 100 is ${sqr(10)}"
打印结果:
Sum of even numbers for 1 to 100 is 2550
Product of even numbers for 1 to 100 is 3840
Squares of even numbers for 1 to 100 is [2, 4, 6, 8, 10]
上面的例子中,进行循环的代码是相同的。差别在于处理的是和、积还是集合。如果想在偶数上执行一些其他操作,还要重复这些遍历数字的代码。所以得想办法去掉这些重复。
- 从上面共同任务的这一函数入手:当挑选出一个偶数时,直接将其传递给一个代码块来处理。比如可以让代码块简单地打印传入的数字:
def pickEven(n, block) {
for (int i = 2; i <= n; i += 2) {
block(i)
}
}
pickEven(10, { println it })
打印结果:
2
4
6
8
10
pickEven() 是一个高阶函数,即以函数为参数,或返回一个函数作为结果的函数。pickEven() 方法对值进行迭代,但不同的是它将值传递给了一个代码块。在 Groovy 中,这个匿名代码块称为 闭包
。
变量 block 保存了一个指向闭包的引用。可以像传递对象一样传递闭包。变量名没有必要一定命名为 block,可以使用其他任何合法的变量名。
- 上面的例子优化如下:
pickEven(10, { println it })
total1 = 0
pickEven(100, { total1 += it })
println "total1 is $total1"
prod1 = 1
pickEven(10, { prod1 *= it })
println "prod1 is $prod1"
squared1 = []
pickEven(10, { squared1 << it })
println "squared1 is $squared1"
打印结果:
total1 is 2550
prod1 is 3840
squared1 is [2, 4, 6, 8, 10]