Groovy Closure简介
什么是闭包(Closure)在Groovy中,闭包是一段匿名的代码段,它可以有参数,返回值,并且能够赋值给一个变量。闭包中使用的变量可以是在闭包外部定义的,也可以是在闭包内部定义的。 闭包定义的语法如下 {[closureParameters ->] statements} 上面的方括号[]表示闭包的参数可有可无,所以正常的闭包可能包括如下这些类型: {item++} //item变量定义在闭包之外,闭包带有默认参数it {->item++} //item变量定义在闭包之外,并且不带默认参数it {println it} //显示默认参数it的值 {it -> println it} //跟上面的一样,显示默认参数it的值 {name -> println name} //定义参数name,并且显示参数name的值 {String x,int y -> //定义参数x为String类型,y为int类型,通过GString显示出来 println "hey ${x} the value is ${y}" } {reader -> //定义参数reader,这个参数是IO类型的输入参数 def line = reader.readLine() line.trim() } 闭包是一个对象闭包是groovy.lang.Closure的一个实例,所以它可以跟Java中的基础类型int,string,boolean一样,可以用来定义变量,函数参数等。 def aClosure = {println 'Hello Closure!!'} println aClosure instanceof Closure //结果:true def methodWithClosureParam(Closure closure){ closure() } methodWithClosureParam aClosure //结果Hello Closure!! methodWithClosureParam(aClosure) //结果Hello Closure!! methodWithClosureParam{ //结果:This is another closure!! println 'This is another closure!!' } //这里函数调用省略了括号() 注意上面的函数调用,它可以不带括号,也可以带括号。并且实参可以是一个外部已定义的闭包,也可以是一个匿名闭包(最后的一个闭包就是匿名闭包,看到这里,用过gradle编译android的朋友们是否感觉很熟悉,这里只是提示一下,后面会重点说明) 还有一个挺有意思的情况就是,如果一个函数的最后一个参数是闭包,那么可以写成如下形式: def methodOneParam(Closure closure) { closure() } def methodEndWithClosureParam(def x,Closure closure) { println x closure() } methodOneParam{ println 'One parameter' //结果:One parameter } methodEndWithClosureParam('Hello'){ println 'Two parameters' //结果: Hello,Two parameters } 最有意思的是最后一个函数调用,第一个参数是使用括号的,如果最后一个参数是Closure,那么还可以写成如上代码这样的形式。 如何调用闭包def calc = {1+2} println calc() //结果:3 println calc.call() //结果:3 闭包有两种调用方式,一种是直接使用(),另外一种使用.call()来调用。另外从上面代码段可以看出,闭包的返回值为最后一行代码的计算结果。 闭包的参数闭包参数分为三种,一种是普通参数,一种是隐式参数,另一种是可变长度参数。 普通参数可以有一个可选的类型,一个参数名,还有一个可选的默认值 def closure = {int intValue = 0 -> def closure = {intValue = 0 -> def closure = {intValue -> 如果没有明确使用->来定义参数列表,那么这个闭包就定义了一个隐式的参数,这个隐式参数名叫it。 def greeting = { println "Hello ${it}"} greeting('man') //结果:Hello man def greeting = {it-> println "Hello ${it}"} greeting('man') //结果:Hello man def greeting = {-> println "Hello ${it}"} greeting('man') //结果:Caught: groovy.lang.MissingMethodException 最后一个闭包调用会出错,因为参数列表定义为空,所以这个时候没有默认参数it。 在闭包中也可以使用可变长度参数,示例如下 def varParamsClosure = {String... args -> args.join(' ') } println varParamsClosure('Hello','world') //结果: Hello world 委托策略对于闭包来说,委托是一个重要的概念,你可以随时改变委托或者委托策略。 如果想要理解委托,那么我们先要解释一下委托中使用的三个重要的变量:
如果没有理解上面的三个概念,那没有关系,下面看看列子就知道是什么意思了。 this的使用例子 class Enclosing{ void run(){ def whatIsThis = {this} println whatIsThis() == this println this } } def enclosing = new Enclosing() enclosing.run() //结果:true, Enclosing@7f010382 println enclosing //结果:Enclosing@7f010382, 和run()中this的引用地址一致,说明就是同一个变量 class ClosureInInnerClass{ class Inner{ def closure = {this} } void run(){ def inner = new Inner() println inner.closure() == inner } } def closureInInnerClass = new ClosureInInnerClass() closureInInnerClass.run() //结果:true class NestedClosures{ void run(){ def closure = { def nestedClosure = { println this this } println nestedClosure() == this } closure() } } def nestedClosures = new NestedClosures() nestedClosures.run() //结果:NestedClosures@1534f01b, true println nestedClosures //结果:NestedClosures@1534f01b, 跟闭包nestedClosure中的this是一致的 从这个例子中,我们可以看出this跟Java语言中概念一致,就是当前类变量,不管是在闭包里访问,还是在闭包的闭包里访问,都是一致的。 闭包的owner属性 闭包的owner就是直接包括闭包的类或者闭包对象, 也就是说如果闭包直接定义在类中,那么owner就是类对象,如果闭包直接定义在闭包中,那么owner就是闭包对象,看看例子就明白了。 class Enclosing{ void run() { def closure = {owner} println this == closure() } } def enclosing = new Enclosing() enclosing.run() //结果:true,这个时候因为闭包是定义在类方法里面的,也就是类里面的,所以owner等价于this class ClosureInInnerClass{ class Inner{ def closure = {owner} } void run(){ def inner = new Inner() println inner.closure() == inner } } def closureInInnerClass = new ClosureInInnerClass() closureInInnerClass.run() //结果:true,这个时候闭包定义在类Inner中,所以owner是inner对象 class NestedClosures{ void run(){ def closure = { def nestedClosure = { println owner owner } nestedClosure() } println closure() == closure } } def nestedClosures = new NestedClosures() nestedClosures.run() //结果:NestedClosures$_run_closure1@65466a6a, true, 这个时候闭包nestedClosure定义在闭包closure中 println nestedClosures //结果:NestedClosures@2ea227af 闭包的delegate属性 闭包的默认delegate属性是owner,不过你可以更改delegate属性,也可以更改delegate策略。 class Enclosing{ void run() { def clThis = {this} def clOwner = {owner} def clDelegate = {delegate} println clThis() == clOwner() println clOwner() == clDelegate() } } def enclosing = new Enclosing() enclosing.run()// 结果:true,true delegate的使用 class Person{ String name } class Thing{ String name } def person = new Person(name:'Mary') def sayHello = {println "Hello " + delegate.name} sayHello.delegate = person sayHello() //结果: Hello Mary def thing = new Thing(name:'Cat') sayHello.delegate = thing sayHello() //结果: Hello Cat 从这里看出,定义闭包的时候,name属性还没有定义,闭包是通过delegate的对象来获取该对象的name属性的。 delegate的策略 class Person{ String name def closure = {println "Hello " + name} } class Thing{ String name } def person = new Person(name:'Mary') person.closure() //结果:Hello Mary def thing = new Thing(name:'Cat') person.closure.delegate = thing person.closure() //结果:Hello Mary, 这是因为闭包的delegate策略默认是owner优先, 由于closure的owner是person这个对象,所以取的name值为Mary, 不过如果这个属性在owner不存在的话,那么会去delegate对象中去找。 请看下面这个例子 class Person{ String name def closure = {println "${name} is ${age} years old"} } class Thing{ String name int age } def person = new Person(name:'Mary') def thing = new Thing(name:'Cat',age:2) person.closure.delegate = thing person.closure() //结果: Mary is 2 years old, 从这里可以看出name值来自person对象, age值来自thing对象 person.closure.resolveStrategy = Closure.DELEGATE_FIRST person.closure() //结果:Cat is 2 years old,改变闭包的委托策略,变成委托优先, 那么这个时候name和age的属性都来之thing变量 闭包的委托策略类型有如下几种:
上面的例子虽然将thing对象委托给闭包,但是因为委托策略为OWNER_ONLY,所以,即使delegate对象中存在age属性,但是这个闭包还是不能访问。 有了这些Groovy语言基础,再去看gradle脚本,应该会感觉容易些。而且如果碰到不清楚的地方,直接去查看DSL文档就行,毕竟gradle脚本实际上就是一些API的调用。在下面的一篇文章中会简单介绍如何去查看DSL的API,以及gradle工具是如何去解析定义在build.gradle文件的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |