Groovy中的闭包
groovy中的一个核心语法:closurs,也叫闭包。闭包在groovy中是一个处于代码上下文中的开放的,匿名代码块。它可以访问到其外部的变量或方法。 1. 句法1.1 定义一个闭包{?[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() } 1.2 闭包也是对象闭包在groovy中是 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')????????????????????? } 1.3 闭包的调用闭包有两种调用方式: def?code?=?{?123?}assert?code()?==?123assert?code.call()?==?123 闭包名+()或者闭包名.call()来调用闭包。 2. 参数2.1 正常参数闭包的参数类型和前面讲的方法的参数类型一样,这里不多说。 2.2 含蓄的参数当闭包没有显式声明参数时,其默认包含一个隐式的参数 def?greeting?=?{?"Hello,?$it!"?}assert?greeting('Patrick')?==?'Hello,?Patrick!' 2.3 参数列表参数列表的用法与普通方法一样,这里不多赘述。 3. 委托策略委托策略是groovy中闭包独有的语法,这也使得闭包较java的lambda更为高级。下面简单介绍一下groovy中的委托策略。 3.1 Owner,delegate和this在理解delegate之前,首先先要了解一下闭包中this和owner的含义,闭包中三者是这么定义的:
3.1.1 This闭包中,使用 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代表的类。 3.1.2 Ownerowner与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。 3.1.3 Delegate闭包中可以使用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?=?passert?upperCasedName()?==?'NORMAN'upperCasedName.delegate?=?tassert?upperCasedName()?==?'TEAPOT' 3.1.4 Delegate策略在闭包中,当一个属性没有指明其所有者的时候,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的用法: 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_FIRSTassert?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?=?passert?cl()?==?42cl.delegate?=?tassert?cl()?==?42cl.resolveStrategy?=?Closure.DELEGATE_ONLY cl.delegate?=?passert?cl()?==?42cl.delegate?=?ttry?{ ????cl()????assert?false}?catch?(MissingPropertyException?ex)?{????//?"age"?is?not?defined?on?the?delegate} 当使用了Closure.DELEGATE_ONLY后,若delegate中找不到age属性,则会直接报错。 4. GStrings中的闭包先来看一下下面这段代码: def?x?=?1def?gs?=?"x?=?${x}"assert?gs?==?'x?=?1' OK,运行没有问题,那如果加两行代码呢? x?=?2assert?gs?==?'x?=?2' 这里就会报错了,错误原因有两:
所以当给x赋值2的时候,gs已经被创建,表达式也已经被计算,结果是x = 1,所以gs得值就是固定的x = 1。 如果要在GString使用闭包也是可以的,如下: def?x?=?1def?gs?=?"x?=?${->?x}"assert?gs?==?'x?=?1'x?=?2assert?gs?==?'x?=?2' (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |