groovy 闭包
语法规则
{ [closureParameters -> ] statements }
下面的语句都是正确的
1 { item++ } 2 { -> item++ } 3 { println it } 4 { it -> println it } 5 { name -> println name } 6 { String x, int y -> println "hey ${x} the value is ${y}" } 7 { reader -> 8 def line = reader.readLine() 9 line.trim() 10 }
参数列表
1 def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b } 2 assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
可以看到参数列表使用逗号分隔,而且 typed 和 untyped 均可,-> 符号用以分隔参数列表和语句。值得提一下,如果闭包只有一个参数,那么可以省略参数列表,直接使用 it 代替参数。
委托策略
闭包有三个概念:this、owner 和 delegate
this 定义闭包的封闭类(enclosing class)
owner 定义闭包的封闭对象(enclosing object),可能是类也可能是对象
delegate 被调用方法或者引用属性的第三方对象,这个需要具体参考代码
this
1 class Enclosing { 2 void run() { 3 def whatIsThisObject = { getThisObject() } // 返回对象 this 4 assert whatIsThisObject() == this 5 def whatIsThis = { this } // 返回对象 this 6 assert whatIsThis() == this 7 } 8 } 9 class EnclosedInInnerClass { 10 class Inner { 11 Closure cl = { this } // Inner 类的对象 this 12 } 13 void run() { 14 def inner = new Inner() 15 assert inner.cl() == inner 16 } 17 } 18 class NestedClosures { 19 void run() { 20 def nestedClosures = { 21 def cl = { this } 22 cl() // 还是 this 23 } 24 assert nestedClosures() == this 25 } 26 }
可以看出 groovy 闭包中的 this 和 java 是一致的。
owner
1 class Enclosing { 2 void run() { 3 def whatIsOwnerMethod = { getOwner() } // 对象的 this 4 assert whatIsOwnerMethod() == this 5 def whatIsOwner = { owner } // 同样是对象的 this 6 assert whatIsOwner() == this 7 } 8 } 9 class EnclosedInInnerClass { 10 class Inner { 11 Closure cl = { owner } // 本质上还是对象的 this 12 } 13 void run() { 14 def inner = new Inner() 15 assert inner.cl() == inner 16 } 17 } 18 class NestedClosures { 19 void run() { 20 def nestedClosures = { 21 def cl = { owner } 22 cl() 23 } 24 assert nestedClosures() == nestedClosures // 此处就是 this 就有区别了,和 owner 的语义一致 25 } 26 }
delegate
上面的 this 和 owner 引用的都是闭包 scope 的内容,但是 delegate 可以引用第三方对象的内容,默认 delegate 被设置为 owner,因为第三方对象不一定被设置。
1 class Person { 2 String name 3 } 4 class Thing { 5 String name 6 } 7 8 def p = new Person(name: 'Norman') 9 def t = new Thing(name: 'Teapot') 10 11 def upperCasedName = { delegate.name.toUpperCase() } // delegate 可以省略 name.toUpperCase() 也是正确的 12 13 upperCasedName.delegate = p 14 assert upperCasedName() == 'NORMAN' 15 upperCasedName.delegate = t 16 assert upperCasedName() == 'TEAPOT' 17 18 // 和引用外部变量有本质区别,下面的 target 不可能被省略 19 def target = p 20 def upperCasedNameUsingVar = { target.name.toUpperCase() } 21 assert upperCasedNameUsingVar() == 'NORMAN'
修改策略
“默认 delegate 被设置为 owner”,这句话隐含意思就是 delegate 的策略可以被调整,有下面四种策略,
Closure.OWNER_FIRST 默认的
Closure.DELEGATE_FIRST delegate 优先
Closure.OWNER_ONLY 只取 owner
Closure.DELEGATE_ONLY 只取 delegate
Closure.TO_SELF 如果用户自己实现 Closure 的子类,这个才有意义
1 class Person { 2 String name 3 int age 4 def fetchAge = { age } 5 } 6 class Thing { 7 String name 8 } 9 10 def p = new Person(name:'Jessica', age:42) 11 def t = new Thing(name:'Printer') 12 def cl = p.fetchAge 13 cl.delegate = p 14 assert cl() == 42 15 cl.delegate = t 16 assert cl() == 42 // 默认情况下 Closure.OWNER_FIRST,所以此处断言是通过的 17 cl.resolveStrategy = Closure.DELEGATE_ONLY 18 cl.delegate = p 19 assert cl() == 42 20 cl.delegate = t 21 try { 22 cl() // 只取 delegate,Thing 类无 age 字段,异常 23 assert false 24 } catch (MissingPropertyException ex) { 25 // 异常会在这里被捕获 26 }
以上内容均参考 http://www.groovy-lang.org/closures.html,文档中还讲到了 closures in GStrings、currying 等很有意思的内容。