6. 闭包(Closure)
闭包其实就是一段代码,但他们又是封装成一个Closure对象。可以类比为java的内部类,或者更好的是C++中的函数对象又叫仿函数(functor)。
闭包使得groovy的代码看起来更简洁,精炼。另外闭包可以使得资源处理更为简便。
例如:
new File(/testfile.txt/).eachLine {println it} // eachLine handle file close automatically
其中的it是闭包默认的变量名。
6.1 声明闭包
前面都是简单的在用的时候直接声明闭包,其实也可以将闭包复制给一个变量,便于多处使用,甚至引用一个已存在对象的方法。闭包也支持默认参数。
def linePrinter = {println it} // assign to a variable
linePrinter('this line') // call
class Foo {
void bar(int value) {
println "call int bar(int value), with params: $value"
}
void bar(List lst) {
println "call int bar(List lst), with params: $lst"
}
void bar(int x, int y) {
println "call int bar(int x, int y), with params: $x, $y"
}
}
Foo foo = new Foo()
foo.bar(10) // normal call
Closure cls = foo.&bar // reference to foo
cls(3) // call by overload detected
cls([1,5,7])
cls(1,2)
def benchmark(repeat, Closure alg) {
start = System.currentTimeMillis();
repeat.times { alg(it) }
return System.currentTimeMillis() - start
}
slow = benchmark(10000) { (int) it / 2 }
fast = benchmark(10000, { it.intdiv(2) })
// intdiv is faster than /
System.out.println("slow - fast = " + (slow - fast)) // output: slow - fast = 20
// default value
def inc = { x, i = 1 -> return x + i }
println inc(3) // output: 4
println inc(5, 2) // output: 7
6.2 闭包自身的方法
还记得C++的函数适配器吗?groovy也提供了这样的方法为Closer:curry(),可接受一个或多个参数。
之前已经看到了isCase方法,以进行分类。
def multiply = { x, y -> return x * y }
multiply.getParameterTypes().size() // result: 2
def double_ = multiply.curry(2)
println double_(3) // output: 6
[1, 2, 8, 7, 3].grep { it % 2 == 0 } // result: [2, 8]
switch(8) {
case { it > 3 } : println "> 3" // output: > 3
}
6.3 从闭包返回结果
闭包有两种返回结果的方式:
a. 返回最后一个表达式执行的结果,在这种情况下return是可以省略的;
b. 从闭包的任何地方返回,注意这种返回的意思是,本次计算结束,也就意味着如果闭包用在循环中,下次的循环的元素将继续被调用;
[1, 2, 3].collect { it * 2 } // same as: return it * 2,result: [2, 4, 6]
[1, 2, 3].collect {
if ( it % 2 == 0) return it * 2 // end current computing
return it
} // result: [1, 4, 3]
6.4 闭包的变量作用域
关于变量的作用域,理解内部类就很容易理解了。我直接引用书上的一个例子,因为想不出更好的例子了。
注意:书上的一个错误:
class Mother {
int field = 1
int foo() {
return 2
}
Closure birth (param) {
def local = 3
def closure = { caller ->
[ this, field, foo(), local, param, caller, owner ] // error in book: this.owner
}
return closure
}
}
Mother julia = new Mother()
closure = julia.birth(4)
context = closure.call(this)
println context[0] == julia // output: ture
println context[1..4] // output: [1, 2, 3, 4]
println context[5] instanceof Script // output: true
println context[6] == julia // output: true
firstClosure = julia.birth(4)
secondClosure = julia.birth(4)
println firstClosure.is(secondClosure) // output: false
特别要注意到是最后一个输出false,每次调用birth都生成了一新的Closure对象。