闭包定义
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
彼得·兰丁在1964年将术语“闭包”定义为一种包含环境成分和控制成分的实体。用来指代某些其开放绑定(自由变量)已经由其语法环境完成闭合(或者绑定)的lambda表达式,从而形成了闭合的表达式,或称闭包。
闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。所谓引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。其中的约束是指一个变量的名字和其所代表的对象之间的联系。
在函数定义时捕获当时的引用环境,并与函数代码组合成一个整体。当把这个整体当作函数调用时,先把其中的引用环境覆盖到当前的引用环境上,然后执行具体代码,并在调用结束后恢复原来的引用环境。这样就保证了函数定义和执行时的引用环境是相同的。这种由引用环境与函数代码组成的实体就是闭包。
自由变量是指除局部变量以外的变量。
Groovy闭包定义
Groovy中的闭包是一个开放的、匿名代码块,它可以接收参数,定义返回值,也可以将闭包复制给变量,闭包还可以引用定义在其周围范围(in its surrounding scope)中的变量。与闭包的正式定义相反,Groovy语言中的闭包还可以包含定义在其周围范围之外(outside of its surrounding scope)的自由变量。
Groovy闭包语法
1 | { [closureParameters ->] statements} |
closureParameters是可选的逗号分隔的参数列表,参数可以指定类型(typed)也可不指定类型(untyped)。如果指定了参数,参数后面必须有 ->
,用于分割参数和闭包内语句。
闭包执行时,总是会有返回值。
Groovy闭包委托策略(Delegation strategy)
委托(Delegation)是Groovy闭包中的一个关键特性(key concept),闭包委托策略的可修改使得在Groovy中设计漂亮的领域特定语言(dsl, domain specific language)成为可能。
this、owner、delegate
闭包内有三个内置对象:
- this 对应于定义闭包的封闭类(the enclosing class where the closure is defined),可以在闭包内通过getThisObject()获取
- owner 对应于定义闭包的封闭对象(the enclosing object where the closure is defined),可以是类也可以是闭包,可以在闭包内通过getOwner()获取
- delegate 对应于一个第三方对象(where methods calls or properties are resolved whenever the receiver of the message is not defined),可以在闭包内通过(getDelegate())获取
通过这三个内置对象,闭包可以调用对应对象的属性和方法。
this
对应于定义闭包的封闭类
1 | // 在TestThis类中定义闭包body,并返回闭包的this对象 |
如果闭包在内部类中定义,那么闭包中的 this
对象返回的是内部类的实例对象
1 | class ClosureTest { |
如果闭包A嵌入在类的某个闭包B中时,闭包A中的 this
表示的仍然是类的实例对象
1 | class ClosureTest { |
owner
返回闭包定义所在的封闭对象,可以是类也可以是闭包。
1 | // 普通类 |
delegate
委托是Groovy语言能够构建DSL的关键特性(It is a powerful concept for building domain specific languages in groovy)。delegate
是一个用户自定义的对象。
默认情况下, delegate
等同于 owner
1 | class ClosureTest { |
闭包中的delegate
属性可以被修改为任何对象
1 | class ClosureTest { |
闭包委托策略
在闭包中,如果访问闭包内部未定义的属性或方法时,会涉及到委托策略,委托策略分为下面几种:
- Closure.OWNER_FIRST: 默认策略。优先在
owner
中查找,如果没有找到则在delegate
中查找 - Closure.DELEGATE_FIRST: 优先在
delegate
中查找, 如果没有找到则在owner
中查找 - Closure.OWNER_ONLY: 忽略
delegate
,只在owner
中查找 - Closure.DELEGATE_ONLY: 忽略
owner
, 只在delegate
中查找 - Closure.TO_SELF: 只有在实现自己的闭包子类时才有意义。在需要高级元编程(meta-programming)技术,希望实现自定义的解析策略: 属性或方法的解析既不使用
owner
也不使用delegate
,only on the closure class itself.
DELEGATE_FIRST 与 OWNER_FIRST
1 | class ClosureTest { |
DELEGATE_FIRST 与 DELEGATE_OWNER
1 | class ClosureTest { |
闭包委托策略具有传递性
闭包A内嵌与闭包B时,如果闭包A中的某个属性在闭包B中没有解析到会继续向闭包B的owner
或delegate
中查找。具体是向闭包的B的owner
还是delegate
中查找,由闭包B的委托策略决定
1 | class ClosureTest { |
1 | class ClosureTest { |
闭包策略在Jenkins pipeline中的典型应用
1 | // vars/abc.groovy |