groovy
中闭包的用法是后续开发高级DSL
的基础,这部分是必须要掌握的。下面开始实践!
闭包与闭包变量
闭包在groovy
语言基础模块中被广泛使用。比如数组的基本操作:
比如,each({ ... })
方法,接收一个闭包。闭包是用{ ... }
包括的一个独立的可调用的语句执行体。比如要打印列表中每个元素,可这样调用:
def friends = ['Jack', 'Tom', 'John']
friends.each ({
println "Hello, ${it}"
})
这里声明了一个打印数组当前遍历的元素的闭包。我们说过groovy
的方法调用大多数情况下是可以省略()
的,下面这种写法更简洁:
friends.each {
println "Hello, ${it}"
}
对象上的
each
方法各种具体类型对
Object.each({ ... })
方法有各自的重载实现,比如看下面的例子:
为什么不推荐变量
闭包也可以赋值给一个变量,但是要注意,变量是可以重新赋值的,一般不推荐这样,看下示例:
def friends = ['Jack', 'Tom', 'John']
def sayHello = {
println "Hello, ${it}"
}
// 将闭包变量作为参数传给一个方法
friends.each sayHello
// 直接调用闭包变量指向的闭包,将其作为函数来调用
sayHello 'xiaojuan'
// 可以改变闭包变量的赋值,现在赋值为一个空闭包,看下发生什么
sayHello = {}
// 下面的调用其实什么都不做,没有任何输出
println '空闭包的调用...'
friends.each sayHello
sayHello 'xiaojuan'
闭包类型的声明
推荐用Closure
来声明,一旦这样声明,后续重新赋值为一个非闭包类型,运行时会报错:
Closure c = {}
// 运行时这一行报错:Cannot cast object '123' with class 'java.lang.Integer' to class 'groovy.lang.Closure'
c = 123
// 下面的打印不会被执行
println 'bad!'
闭包与普通代码块的区别
闭包作为方法参数
定义一个接收闭包参数的方法
// 参数c为闭包类型
def sayHiMethod(Closure c) {
c.call()
}
// 闭包参数的调用形式
sayHiMethod { println 'Hi!' }
最后一个参数调用的匿名写法
def friends = ['Jack', 'Tom', 'John']
// 作为最后一个参数,可以从圆括号中脱离出来,实现一种匿名写法
friends.each() {
println "Hello, ${it}"
}
// 从第0个元素开始匹配要找的元素,注意第二个参数是一个对当前元素进行过滤的闭包
def result = friends.findIndexOf(0, { it == 'Tom'})
println result // 输出1
// 也可以从括号中剥离出来,这样写
result = friends.findIndexOf(0) { it == 'John'}
println result // 输出2
这种写法比较怪异,一般还是老老实实写在括号了,看到这样的代码要能理解这种写法即可。
DSL中的闭包参数
模拟对多线程临界区代码的执行,可以封装一个lock
方法,将要执行的代码封装在一个闭包中传进来执行:
转发参数
在定义一个方法时,可以把定义的参数再传给闭包参数进行调用,这就是转发参数。看模拟数据库更新用户信息的一个例子:
// 定义User类
class User {
String name
}
// 模拟执行数据库事务的方法
def doTransaction(Closure c) {
println 'begin transaction...'
c.call()
println 'commit transaction...'
}
// 更新用户方法
def update(user, Closure c) {
println "before update name is $user.name"
// 调用时转发参数
c.call(user)
println "after update name is $user.name"
}
// 实例化一个用户
def juan = new User(name: 'juan')
doTransaction {
update(juan) { user -> user.name = 'xiaojuan' }
}
执行结果:
begin transaction...
before update name is juan
after update name is xiaojuan
commit transaction...