Closures(闭包)

2024-06-24 08:08
文章标签 闭包 closures

本文主要是介绍Closures(闭包),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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() == 123assert 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 namedef 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 nameint agedef 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中闭包的基本用法结束了,更多闭包的用请参考

  • Closures

还记得我们学习groovy的目的是什么吗?对了,就是gradle。而且在gradle中使用了大量闭包的概念,所以在学习gradle之前还请好好掌握闭包这一节内容。

这篇关于Closures(闭包)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089581

相关文章

JavaScript深入理解闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域。 变量的作用域无非就是两种:全局变量和局部变量。 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。 Js代码   var n=999;   function f1

【JavaScript】在循环内使用闭包

================== 基本循环语句 ==================for (var i = 0; i < 5; i++) {console.log(i);}console.log(i);//这个大家应该很快就知道了,012345================== setTimeout与var语句的for循环 ==================for (var i

python闭包的作用

python闭包的作用 Python中的闭包是一种强大的编程概念,它在处理函数和作用域时提供了灵活性和便利性。下面是闭包的一些主要作用和应用: 1. 封装数据和函数 闭包可以将数据和操作这些数据的函数封装在一起,使得外部无法直接访问这些数据。这有助于创建数据的私有性。 def make_counter():count = 0def counter():<

【HDU】1317 XYZZY spfa判负环+floyd求传递闭包

传送门:【HDU】1317 XYZZY 题目分析:首先我们可以用spfa判最长路上是否有正权环,但是有正权环却不等价于能到达终点。这是我们还需要判断是否能从正权环中走到终点,这个可以用传递闭包搞定。如果没有正权环就看是否能从起点到终点就好了。 代码如下: #include <cstdio>#include <cstring>#include <algorithm>

前端组件化(一) 函数闭包 简单版

实现功能: 输入几个字, 就实时显示几个字 截图: 作用域隔离 为了使整个代码的作用域清晰明了, 封装成textCont 函数 <!DOCTYPE HTML><html><head><title>单通道数据流</title></head><body><input type="text" id="J_inp"><span id="J_text"></span></body><script sr

[Python]闭包与装饰器

闭包与装饰器 闭包: ​ python的一种独有写法,可以实现:对外部函数的局部变量进行’临时’存储 作用: ​ 可以"延长"函数内 局部变量的生命周期. 构成条件: 1. 有嵌套. 外部函数内要嵌套 内部函数.2. 有引用. 在内部函数中使用 外部函数的变量.3. 有返回. 在外部函数中, 返回 内部函数名, 即: 内部函数对象 格式def 外部函数

初探swift语言的学习笔记三(闭包-匿名函数)

作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/29353019 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或关注微信公众帐号fengsh998来支持我,谢谢! 很多高级语言都支持匿名函数操作,在OC中的block也为大家所熟悉,然面在swift里好像是被

深入理解JavaScript系列(16):闭包(Closures)

介绍 本章我们将介绍在JavaScript里大家经常来讨论的话题 —— 闭包(closure)。闭包其实大家都已经谈烂了。尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭包内部究竟是如何工作的。 正如在前面的文章中提到的,这些文章都是系列文章,相互之间都是有关联的。因此,为了更好的理解本文要介绍的内容,建议先去阅读第14章作用域链和第12章变量对象。 英

Javascript归纳与总结——this指向及其改变、new关键字与原型链、异步、闭包和函数防抖与节流

this指向及其改变 普通函数在调用时,this为obj.obj1.fun(),this->obj1,箭头函数在声明定义时this->obj。 Javascript中bind、call、apply區別-CSDN博客 new关键字与原型链  从原型链视角解读VueComponent与Vue关系_vue中重要的原型链关系-CSDN博客 prototype这个属性只有函数对象才有!(构造)

python基础-闭包、装饰器

闭包 什么是闭包? 内部函数引用了外部函数的变量 看下面的代码 def f(b):def f1():print(b)f1()print(f1.__closure__)f(1) 输出如下: E:\python\python_sdk\python.exe E:/python/py_pro/demo.py1(<cell at 0x01406390: int object at 0x56A