闭包
原创大约 4 分钟
闭包是一个开放的、匿名的Groovy代码块,可以接受参数分配给变量,并返回数据。
闭包既有Java中Lambda表达式
的特性,但又比它更进一步。
语法
下面是几种定义闭包的方式,当然,它们需要结合上下文环境才能正常运行。
{ item++ }
{ -> item++ }
{ println it }
{ it -> println it }
{ name -> println name }
{ String x, int y ->
println "hey ${x} the value is ${y}"
}
{ reader ->
def line = reader.readLine()
line.trim()
}
闭包是groovy.lang.closure
类的实例,它可以像任何其他语句一样把自己分配给变量或字段。
// 将闭包分配给变量listener
> def listener = { e -> println "listener on $e" }
> println(listener instanceof Closure)
true
// 显式定义一个闭包
> Closure callback = {
> println 'callback'
> }
// 显式定义一个闭包
> Closure<Boolean> isTextFile = {
> File it -> it.name.endsWith('.txt')
> }
// 调用闭包
// .call()和.()调用方式等价
// println(listener.call('lixingyun'))
> println(listener('lixingyun'))
listener on lixingyun
null
// println(callback.call())
> println(callback())
callback
null
// println(isTextFile.call(new File('readme.md')))
> println(isTextFile(new File('readme.md')))
false
参数
闭包的参数和常规方法一样,有可选的类型、有名字、有可选的默认值,并且用,
分隔。
// 只有一个参数的闭包,没有指定类型
> def c1 = { str -> str.toUpperCase() }
> println(c1('lixingyun'))
LIXINGYUN
// 只有一个参数的闭包,明确指定类型
> def c2 = { String str -> str.toUpperCase() }
> println(c2('lixingyun'))
LIXINGYUN
// 有两个参数的闭包,没有指定类型
> def c3 = { a, b -> a + b }
> println(c3(1, 2))
3
// 有两个参数的闭包,明确指定类型
> def c4 = { int a, int b -> a + b }
> println(c4(1, 2))
3
// 有两个参数的闭包,一个指定类型,一个不指定类型
> def c5 = { a, int b -> a + b }
> println(c5(1, 2))
3
// 有两个参数的闭包,明确指定类型,且其中一个有默认值
> def c6 = { int a, int b = 2 -> a + b }
> println(c6(1))
3
闭包还允许存在隐式参数。
> def greeting = { "Hello $it" }
> println(greeting('lixingyun'))
Hello lixingyun
// 闭包的隐式参数名称固定为it,上下两段代码完全等价
> def g1 = { it -> "Hello $it" }
> println(g1('lixingyun'))
Hello lixingyun
> def magicNumber = { -> 42 }
// println(magicNumber(11)) 这样调用会失败,因为闭包此时不接受任何参数
> println(magicNumber())
42
闭包同样使用可变参数。
> def concat1 = { String... args -> args.join('-') }
> println(concat1('abc', 'def', 'gh'))
abc-def-gh
> def concat2 = { String[] args -> args.join('-') }
> println(concat2('abc', 'def'))
abc-def
> def multiConcat = { int n, String... args ->
> args.join('-') * n
> }
> println(multiConcat(2, 'abc', 'def'))
abc-defabc-def
委托策略
Delegation
(委托)是Groovy闭包中的一个关键概念,在Lambda表达式
中没有对应的概念。
更改闭包的Delegation Strategy
(委托策略)的能力使得在Groovy中设计并实现领域特定语言(Domain Specific Language,DSL)
成为可能。
在闭包中调用getThisObject()
方法将返回定义闭包的封闭类,这相当于显式调用this
。
> // 三种不同的调用getThisObject()或this的情况
> class Enclosing {
> void run() {
> def whatIsThisObject = { getThisObject() }
> println(whatIsThisObject() == this)
> def whatIsThis = { this }
> println(whatIsThis() == this)
> }
> }
> def enclosing = new Enclosing()
> enclosing.run()
Enclosing@675d8c96
Enclosing@675d8c96
> class EnclosedInInnerClass {
> class Inner {
> Closure closure = { this }
> }
> void run() {
> def inner = new Inner()
> println(inner.closure() == inner)
> }
> }
> def enclosedInInnerClass = new EnclosedInInnerClass()
> enclosedInInnerClass.run()
EnclosedInInnerClass$Inner@274872f8
> class NestedClosures {
> void run() {
> def nestedClosures = {
> def cl = { this }
> cl()
> }
> println(nestedClosures() == this)
> }
> }
> def nestedClosures = new NestedClosures()
> nestedClosures.run()
NestedClosures@42b64ab8
> class Person {
> String name
> int age
> String toString() { "$name is $age years old" }
> String dump() {
> def cl = {
> this.toString()
> }
> cl()
> }
> }
> def person = new Person(name: 'lixingyun', age: 19)
> println(person.dump())
lixingyun is 19 years old
闭包的Owner
(所有者)与闭包中的this
的非常相似,但又稍有不同。
> // 三种不同的调用getOwner()或owner的情况
> class Enclosing {
> void run() {
> def whatIsThisObject = { getOwner() }
> println(whatIsThisObject())
> def whatIsThis = { owner }
> println(whatIsThis())
> }
> }
> def enclosing = new Enclosing()
> enclosing.run()
Enclosing@74e47444
Enclosing@74e47444
> class EnclosedInInnerClass {
> class Inner {
> Closure closure = { owner }
> }
> void run() {
> def inner = new Inner()
> println(inner.closure())
> }
> }
> def enclosedInInnerClass = new EnclosedInInnerClass()
> enclosedInInnerClass.run()
EnclosedInInnerClass$Inner@7383eae2
> class NestedClosures {
> void run() {
> def nestedClosures = {
> def cl = { owner }
> cl()
> }
> println(nestedClosures())
> }
> }
> def nestedClosures = new NestedClosures()
// 除了这里和调用this明显不同,其他的没什么区别
> nestedClosures.run()
NestedClosures$_run_closure1@72a85671
> class Person {
> String name
> int age
> String toString() { "$name is $age years old" }
> String dump() {
> def cl = {
> owner.toString()
> }
> cl()
> }
> }
> def person = new Person(name: 'lixingyun', age: 19)
> println(person.dump())
lixingyun is 19 years old
闭包的委托可以变成为任意的对象。
> class Person { String name }
> class Leader { String name }
> def person = new Person(name: 'lixingyun')
> def leader = new Leader(name: 'dashuai')
> // 用闭包来获取委托的name属性
> def upperCasedName = { delegate.name.toUpperCase() }
> upperCasedName.delegate = person
> println(upperCasedName())
LIXINGYUN
> upperCasedName.delegate = leader
> println(upperCasedName())
DASHUAI
函数式编程
Groovy的闭包中所谓的Currying
(柯里化)和通常所说的柯里化的含义不同。
> def copies = { int n, String str -> str * n }
// 柯里化:将多个参数合并成1个
// 左柯里化(左和右是相对于参数的位置而言的)
> def twice = copies.curry(2)
> println(twice('bla'))
blabla
// 右柯里化(左和右是相对于参数的位置而言的)
> def blah = copies.rcurry('bla')
> println(blah(2))
blabla
> println(copies(2, 'bla'))
blabla
如果只有两参数可以区分出左
和右
,那有两个以上的参数怎么办?可以通过基于索引的柯里化
实现。
> def volume = { double l, double w, double h -> l * w * h }
// 只把第2个参数(n=1)设置为2
> def fixedWidthVolume = volume.ncurry(1, 2d)
> println(volume(3d, 2d, 4d))
24.0
> println(fixedWidthVolume(3d, 4d))
24.0
// 从指定的索引(n=1)开始设置多个参数,这里相当于指定了后两个
> def fixedWidthAndHeight = volume.ncurry(1, 2d, 4d)
> println(volume(3d, 2d, 4d))
24.0
> println(fixedWidthAndHeight(3d))
24.0
感谢支持
更多内容,请移步《超级个体》。