闭包概述
闭包就是一个特殊的匿名代码块,可以传递参数,有返回值,还能作为方法的参数进行传递。
闭包格式
闭包的格式定义如下:
{ [closureParameters -> ] statements }
示例:
{ 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() }
closureParameters :闭包参数是非必需的,与方法的参数十分类似,区别是:如果存在闭包参数,闭包参数与闭包语句之间需要使用箭头(->)的分割。
注意:如果没有指定参数,则默认存在一个it的参数,代表的是闭包本身。如下例子:
def obj = { println(it) } obj('hello')
statements:同样,闭包语句则可以类比于方法体,功能也相同。
变量可作为闭包的载体,原因是:闭包其实是Groovy中的Closure类的实例。如下三种示例:
def listener = { e -> println "Clicked on $e.source" } //指明为Closure类型实例 Closure callback = { println 'Done!' } //指明为Closure类型实例,并指定返回类型 Closure<Boolean> isTextFile = { File it -> it.name.endsWith('.txt') }
执行闭包有两种方式:一是直接调用;二是通过调用Closure的call方法。如下代码:
def obj = { def item = 10 return ++item } // 直接调用 println(obj()) //call调用 println(obj.call())
从字节码来看上述两种方式的区别:两种方式完全等效,都是通过call来完成
public Object test() { CallSite[] var1 = $getCallSiteArray(); class _test_closure1 extends Closure implements GeneratedClosure { public _test_closure1(Object _thisObject) { CallSite[] var3 = $getCallSiteArray(); super(Closures.this, _thisObject); } public Object doCall(Object it) { CallSite[] var2 = $getCallSiteArray(); Object item = Integer.valueOf(10); return var2[0].call(item); } public Object doCall() { CallSite[] var1 = $getCallSiteArray(); return this.doCall((Object)null); } } Object obj = new _test_closure1(this); var1[2].callCurrent(this, var1[3].call(obj)); return var1[4].callCurrent(this, var1[5].call(obj)); }
代理策略
代理在groovy的闭包中是非常重要的概念,与之相关的是this、owner和delegate这三种概念。
this:指的是定义闭包所在的直接的外围类,具体有如下两种方式获取:
class Test { void run() { // 方式一:使用getThisObject def enclosingThisObject = { getThisObject() } // 方式二:this操作符 def enclosingThis = { this } def testThis = this def print = "enclosingThisObject=${enclosingThisObject()},enclosingThis=${enclosingThis()},testThis=$testThis" println(print) } } def test = new Test() test.run()
运行结果:
enclosingThisObject=com.demo.test.cl.Test@4f209819, enclosingThis=com.demo.test.cl.Test@4f209819, testThis=com.demo.test.cl.Test@4f209819
从字节码也可看出两种方式是等效的,因为this方式也会调用getThisObject,如下:
public void run() { CallSite[] var1 = $getCallSiteArray(); class _run_closure1 extends Closure implements GeneratedClosure { public _run_closure1(Object _thisObject) { CallSite[] var3 = $getCallSiteArray(); super(Test.this, _thisObject); } public Object doCall(Object it) { CallSite[] var2 = $getCallSiteArray(); return var2[0].callCurrent(this); } public Object doCall() { CallSite[] var1 = $getCallSiteArray(); return this.doCall((Object)null); } } Object enclosingThisObject = new _run_closure1(this); class _run_closure2 extends Closure implements GeneratedClosure { public _run_closure2(Object _thisObject) { CallSite[] var3 = $getCallSiteArray(); super(Test.this, _thisObject); } public Object doCall(Object it) { CallSite[] var2 = $getCallSiteArray(); // this方式调用getThisObject方法 return this.getThisObject(); } public Object doCall() { CallSite[] var1 = $getCallSiteArray(); return this.doCall((Object)null); } } Object enclosingThis = new _run_closure2(this); Object print = new GStringImpl(new Object[]{var1[0].call(enclosingThisObject), var1[1].call(enclosingThis), this}, new String[]{"enclosingThisObject=", ",enclosingThis=", ",testThis=", ""}); var1[2].callCurrent(this, print); }
注意:在内部类中定义闭包,那么this指的是这个内部类;同样,嵌套的闭包定义,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其实是闭包与所在外部类的通信桥梁。
owner: 指的是定义闭包的直接外围对象,可以是类或者闭包。与this相似,区别在于嵌套闭包,如下实例:
class NestedClosures { void runOwner() { def nestedClosures = { def cl = { owner } cl() } assert nestedClosures() == nestedClosures } void runThis() { def nestedClosures = { def cl = { this } cl() } assert nestedClosures() == this } }
嵌套闭包中this与owner的区别:this是外围类的实例,而owner则是外层闭包实例。
小结:闭包中的this侧重于直接外围类,而owner则偏向于闭包的宿主对象,它们都是闭包与外部环境的通信桥梁。
delegate:获取闭包的delegate有如下两种方式,其返回的默认值是owner。
// 方式一:调用getDelegate()方法 def obj1 = { getDelegate() } // 方式二:delegate关键字 def obj2 = { delegate } // delegate默认值是owner println(obj1() == obj2.owner) // 嵌套闭包delegate默认值也是owner def enclosed = { { -> delegate }.call() } assert enclosed() == enclosed
如下通过不同的代理对象,闭包的行为是不同的:
class Person { String name } class Thing { String name } def p = new Person(name: 'Norman') def t = new Thing(name: 'Teapot') def upperCasedName = { delegate.name.toUpperCase() } upperCasedName.delegate = p println(upperCasedName())//Norman upperCasedName.delegate = t println(upperCasedName())//Teapot
代理策略:上述例子中的闭包能正常运行,是因为闭包的代理策略影响了编译闭包代码时的解析策略。分为如下几种策略:
- Closure.OWNER_FIRST:OWNER优先策略,也是默认策略,优先从owner中寻找属性或方法,找不到再从delegete中寻找。
- Closure.DELEGATE_FIRST:与OWNER_FIRST相反。
- Closure.OWNER_ONLY: 只在owner中寻找属性或方法。
- Closure.DELEGATE_ONLY: 只在delegate中寻找属性或方法。
- Closure.TO_SELF: 前提是用户需要实现的Closure的子类,只会在闭包自身中寻找属性或方法。
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的默认代理对象就是p,可以省略 // cl.delegate = p println(cl())//42 cl.delegate = t println(cl())//42 //修改代理策略 cl.resolveStrategy = Closure.DELEGATE_ONLY cl.delegate = p println(cl())//42 cl.delegate = t try { cl() assert false } catch (MissingPropertyException ex) { // "age" is not defined on the delegate }
最后
以上就是孤独奇迹最近收集整理的关于Groovy语法之闭包的全部内容,更多相关Groovy语法之闭包内容请搜索靠谱客的其他文章。
发表评论 取消回复