本节主要讲groovy中的一个核心语法:closurs,也叫闭包。闭包在groovy中是一个处于代码上下文中的开放的,匿名代码块。它能够访问到其外部的变量或方法。html
{ [closureParameters -> ] statements }
其中[]
内是可选的闭包参数,可省略。当闭包带有参数,就须要->
来将参数和闭包体相分离。java
下面看一些闭包的具体例子:闭包
{ 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中是groovy.lang.Closure
类的实例,这使得闭包能够赋值给变量或字段。学习
def listener = { e -> println "Clicked on $e.source" } assert listener instanceof Closure Closure callback = { println 'Done!' } Closure<Boolean> isTextFile = { File it -> it.name.endsWith('.txt') }
闭包有两种调用方式:fetch
def code = { 123 } assert code() == 123 assert code.call() == 123
闭包名+()或者闭包名.call()来调用闭包。gradle
闭包的参数类型和前面讲的方法的参数类型同样,这里很少说。this
当闭包没有显式声明参数时,其默认包含一个隐式的参数it
。code
def greeting = { "Hello, $it!" } assert greeting('Patrick') == 'Hello, Patrick!'
参数列表的用法与普通方法同样,这里很少赘述。orm
委托策略是groovy中闭包独有的语法,这也使得闭包较java的lambda更为高级。下面简单介绍一下groovy中的委托策略。htm
在理解delegate以前,首先先要了解一下闭包中this和owner的含义,闭包中三者是这么定义的:
this
表示定义闭包的外围类。owner
表示定义闭包的直接外围对象,能够是类或者闭包。delegate
表示一个用于处理方法调用和属性处理的第三方类。闭包中,使用this
关键字或者调用方法getThisObject()
来得到其外围类:
class Enclosing { void run() { def whatIsThisObject = { getThisObject() } assert whatIsThisObject() == this def whatIsThis = { this } assert whatIsThis() == this } } class EnclosedInInnerClass { class Inner { Closure cl = { this } } void run() { def inner = new Inner() assert inner.cl() == inner } } class NestedClosures { void run() { def nestedClosures = { def cl = { this } cl() } assert nestedClosures() == this } }
判断this表示的具体是哪一个对象能够从this往外找,遇到的第一类就是this表明的类。
owner与this相似,只不过owner表示的是直接外围对象,能够是类也能够是闭包:
class Enclosing { void run() { def whatIsOwnerMethod = { getOwner() } assert whatIsOwnerMethod() == this def whatIsOwner = { owner } assert whatIsOwner() == this } } class EnclosedInInnerClass { class Inner { Closure cl = { owner } } void run() { def inner = new Inner() assert inner.cl() == inner } } class NestedClosures { void run() { def nestedClosures = { def cl = { owner } cl() } assert nestedClosures() == nestedClosures } }
上述例子与this中的例子不一样的就是NestedClosures,其中owner表示的是nestedClosures而不是NestedClosures。
闭包中可使用delegate关键字或者getDelegate()方法来获得delegate变量,它默认与owner一致,但能够由用户自定义其表明的对象。
class Enclosing { void run() { def cl = { getDelegate() } def cl2 = { delegate } assert cl() == cl2() assert cl() == this def enclosed = { { -> delegate }.call() } assert enclosed() == enclosed } }
闭包中的delegate可被指向任意对象,咱们看下面这个例子:
class Person { String name } class Thing { String name } def p = new Person(name: 'Norman') def t = new Thing(name: 'Teapot')
定义了两个拥有相同属性name的类Person和Thing。接着定义一个闭包,其做用是经过delegate来得到name属性。
def upperCasedName = { delegate.name.toUpperCase() }
接着改变闭包的delegate的指向,咱们能够看到闭包调用结果也不一样:
upperCasedName.delegate = p assert upperCasedName() == 'NORMAN' upperCasedName.delegate = t assert upperCasedName() == 'TEAPOT'
在闭包中,当一个属性没有指明其全部者的时候,delegate策略就会发挥做用了。
class Person { String name } def p = new Person(name:'Igor') def cl = { name.toUpperCase() } //❶ cl.delegate = p //❷ assert cl() == 'IGOR' //❸
能够看到❶处的name没有指明其全部者。即这个name属性压根不知道是谁的。在❷处指明cl的delegate为p,这时候在❸处调用成功。
以上代码之因此能够正常运行是由于name属性会被delegate处理。这是一个十分强大的方式用于解决闭包内的属性的访问或方法的调用。在❶处没有显示的使用delegate.name是由于delegate策略已经在程序运行的时候帮助咱们这样作了。下面咱们看看闭包拥有的不一样的delegate策略:
Closure.OWNER_FIRST
这是默认的策略,优先从owner中寻找属性或方法,找不到再从delegete中寻找。上面的例子就是由于在owner中没有找到name,接着在delegate中找到了name属性。Closure.DELEGATE_FIRST
与OWNER_FIRST相反。Closure.OWNER_ONLY
只在owner中寻找。Closure.DELEGATE_ONLY
只在delegate中寻找。Closure.TO_SELF
在闭包自身中寻找。下面咱们看一下默认的Closure.OWNER_FIRST的用法:
class Person { String name def pretty = { "My name is $name" } String toString() { pretty() } } class Thing { String name } def p = new Person(name: 'Sarah') def t = new Thing(name: 'Teapot') assert p.toString() == 'My name is Sarah' p.pretty.delegate = t //❶ assert p.toString() == 'My name is Sarah' //❷
尽管在❶处将delegate指向了t,但由于是owner first的缘故,仍是会优先使用Person的name属性。
略作修改:
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST assert p.toString() == 'My name is Teapot'
这时候就会访问t的name属性了。
下面再来看一个例子:
class Person { String name int age def fetchAge = { age } } class Thing { String name } def p = new Person(name:'Jessica', age:42) def t = new Thing(name:'Printer') def cl = p.fetchAge cl.delegate = p assert cl() == 42 cl.delegate = t assert cl() == 42 cl.resolveStrategy = Closure.DELEGATE_ONLY cl.delegate = p assert cl() == 42 cl.delegate = t try { cl() assert false } catch (MissingPropertyException ex) { // "age" is not defined on the delegate }
当使用了Closure.DELEGATE_ONLY后,若delegate中找不到age属性,则会直接报错。
先来看一下下面这段代码:
def x = 1 def gs = "x = ${x}" assert gs == 'x = 1'
OK,运行没有问题,那若是加两行代码呢?
x = 2 assert gs == 'x = 2'
这里就会报错了,错误缘由有两:
${x}
这种写法并非一个闭包,而是一个表达式等价于$x
,当GString被建立的时候该表达式会被计算。因此当给x赋值2的时候,gs已经被建立,表达式也已经被计算,结果是x = 1,因此gs得值就是固定的x = 1。
若是要在GString使用闭包也是能够的,以下:
def x = 1 def gs = "x = ${-> x}" assert gs == 'x = 1' x = 2 assert gs == 'x = 2'
到这里groovy中闭包的基本用法结束了,更多闭包的用请参考
还记得咱们学习groovy的目的是什么吗?对了,就是gradle。并且在gradle中使用了大量闭包的概念,因此在学习gradle以前还请好好掌握闭包这一节内容。😀