Closures(闭包)

本节主要讲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中是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')
}

1.3 闭包的调用

闭包有两种调用方式:

def code = { 123 }

assert code() == 123

assert code.call() == 123

闭包名+()或者闭包名.call()来调用闭包。

2. 参数

2.1 正常参数

闭包的参数类型和前面讲的方法的参数类型一样,这里不多说。

2.2 含蓄的参数

当闭包没有显式声明参数时,其默认包含一个隐式的参数it

def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

2.3 参数列表

参数列表的用法与普通方法一样,这里不多赘述。

3. 委托策略

委托策略是groovy中闭包独有的语法,这也使得闭包较java的lambda更为高级。下面简单介绍一下groovy中的委托策略。

3.1 Owner,delegate和this

在理解delegate之前,首先先要了解一下闭包中this和owner的含义,闭包中三者是这么定义的:

  • this 表示定义闭包的外围类。
  • owner 表示定义闭包的直接外围对象,可以是类或者闭包。
  • delegate 表示一个用于处理方法调用和属性处理的第三方类。

3.1.1 This

闭包中,使用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代表的类。

3.1.2 Owner

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。

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 = p
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert 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 这是默认的策略,优先从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属性,则会直接报错。

4. GStrings中的闭包

先来看一下下面这段代码:

def x = 1
def gs = "x = ${x}"
assert gs == 'x = 1'

OK,运行没有问题,那如果加两行代码呢?

x = 2
assert gs == 'x = 2'

这里就会报错了,错误原因有两:

  • GString只是调用了字符串的toString方法来获得值。
  • ${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之前还请好好掌握闭包这一节内容。

Groovy中的闭包的更多相关文章

  1. Gradle中的闭包

    Gradle是基于Groovy的DSL基础上的构建工具,Gradle中的闭包,其原型上实际上即Groovy中闭包.而在表现形式上,其实,Gradle更多的是以约定和基于约定基础上的配置去展现.但本质上 ...

  2. Groovy中的面向对象

    Groovy中的面向对象 前面说到groovy支持脚本和类,前面一节已将简单说了脚本和类之间的关系,这一节主要介绍一下groovy中类的相关知识,即面向对象相关知识. 1.类型 1.1 原始类型 gr ...

  3. Groovy中each、find跳出循环

    在groovy中使用break跳出each或者find的循环会会报错,为什么呢?groovy中each.find方法是一个闭包操作,要想跳出循环要使用 return true,但有几个问题有待研究: ...

  4. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  5. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  6. 难道这就是JavaScript中的"闭包"

    其实对于JavaScript中的"闭包"还没真正理解,这次在实际Coding中似乎遇到了"闭包"的问题,仅此摘录,以待深究. 表现为jQuery的post方法回 ...

  7. 【原】如何在jQuery中实现闭包

    原生JS中,闭包虽好用,但是很难用好,在jQuery中一样,都有一些点需要我们注意.jQuery中使用闭包的常见情况有以下几种: 1.$(document).ready()的参数 我们在写jQuery ...

  8. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...

  9. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

随机推荐

  1. node项目部署相关问题

    process.env process.env属性返回一个对象,包含了当前Shell的所有环境变量. 通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为produ ...

  2. Linguistic Data Consortium (LDC)

    搞NLP的人经常会听到一个神秘的名字LDC,因为大量的论文所使用的数据都来自于LDC,本文就来揭露其神秘面目. About LDC: LDC,全名Linguistic Data Consortium, ...

  3. STM32下多串口用法

    一个项目用到32下的多个串口,一般STM32C8T6型号拥有3个USART,串口的配置都很简单,但是要使用的话就得解决他们之间的矛盾, printf函数到底输出在哪一个串口中? 先看这函数: //重定 ...

  4. 那些你觉得堪称神兵利器的 Chrome 插件

    子曾曰:"工欲善其事,必先利其器.居是邦也."--语出<论语·卫灵公>:其后一百多年,荀子也在其<劝学>中倡言道:"吾尝终日而思矣,不如须臾之所学 ...

  5. POJ-1887 Testing the CATCHER(dp,最长下降子序列)

    Testing the CATCHER Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 16515 Accepted: 6082 ...

  6. 在排序模型方面,点评搜索也经历了业界比较普遍的迭代过程:从早期的线性模型LR,到引入自动二阶交叉特征的FM和FFM,到非线性树模型GBDT和GBDT+LR,到最近全面迁移至大规模深度学习排序模型。

    https://mp.weixin.qq.com/s/wjgoH6-eJQDL1KUQD3aQUQ 大众点评搜索基于知识图谱的深度学习排序实践 原创: 非易 祝升 仲远 美团技术团队 前天    

  7. hmm用于speech和image

    隐马尔科夫模型用于speech和image的原因是,因为hmm模型主要是适用于前后特征有关联性(参考骰子案例)的数据,有三种模式, 其中一种模式就是通过数据输出判断来源分类,而speech和image ...

  8. Tautology---poj3295(枚举判断是否为永真式)

    题目链接:http://poj.org/problem?id=3295 题意:判断是否是永真式,其中 p q r s t 表示逻辑变量其值为0或者1: 枚举所有逻辑变量的值,然后判断是否出现false ...

  9. linux环境下python的部署

    linux系统环境自带python2.6,但有时我们项目使用的版本可能是3.x以上等等,此时我们需要在linux中再安装项目所需的python版本,此时就涉及多版本共存问题了,很多同学在安装多个版本P ...

  10. hammerjs wabapp h5 触屏手势一网打尽

    hammerjs官网    http://hammerjs.github.io/ 学习文章1 http://www.cnblogs.com/vajoy/p/4011723.html 学习文章2 htt ...