groovy之closure

2024-06-24 08:08
文章标签 groovy closure

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

什么是闭包(Closure)

在Groovy中,闭包是一段匿名的代码段,它可以有参数,返回值,并且能够赋值给一个变量。闭包中使用的变量可以是在闭包外部定义的,也可以是在闭包内部定义的。

闭包定义的语法如下

{[closureParameters ->] statements}

上面的方括号[]表示闭包的参数可有可无,所以正常的闭包可能包括如下这些类型:

{item++} //item变量定义在闭包之外,闭包带有默认参数it{->item++} //item变量定义在闭包之外,并且不带默认参数it{println it} //显示默认参数it的值{it -> println it} //跟上面的一样,显示默认参数it的值{name -> println name} //定义参数name,并且显示参数name的值{String x, int y -> //定义参数x为String类型,y为int类型,通过GString显示出来println "hey ${x} the value is ${y}"
}{reader -> //定义参数reader,这个参数是IO类型的输入参数def line = reader.readLine()line.trim()
}

闭包是一个对象

闭包是groovy.lang.Closure的一个实例,所以它可以跟Java中的基础类型int,string,boolean一样,可以用来定义变量,函数参数等。

def aClosure = {println 'Hello Closure!!'}
println aClosure instanceof Closure //结果:truedef methodWithClosureParam(Closure closure){closure()
}methodWithClosureParam aClosure //结果Hello Closure!!
methodWithClosureParam(aClosure) //结果Hello Closure!!
methodWithClosureParam{ //结果:This is another closure!!println 'This is another closure!!'
} //这里函数调用省略了括号()

注意上面的函数调用,它可以不带括号,也可以带括号。并且实参可以是一个外部已定义的闭包,也可以是一个匿名闭包(最后的一个闭包就是匿名闭包,看到这里,用过gradle编译android的朋友们是否感觉很熟悉,这里只是提示一下,后面会重点说明)

还有一个挺有意思的情况就是,如果一个函数的最后一个参数是闭包,那么可以写成如下形式:

def methodOneParam(Closure closure)
{closure()
}def methodEndWithClosureParam(def x, Closure closure)
{println xclosure()
}methodOneParam{println 'One parameter' //结果:One parameter
}methodEndWithClosureParam('Hello'){println 'Two parameters' //结果: Hello,Two parameters
}

最有意思的是最后一个函数调用,第一个参数是使用括号的,如果最后一个参数是Closure,那么还可以写成如上代码这样的形式。

如何调用闭包

def calc = {1+2}
println calc() //结果:3
println calc.call() //结果:3

闭包有两种调用方式,一种是直接使用(),另外一种使用.call()来调用。另外从上面代码段可以看出,闭包的返回值为最后一行代码的计算结果。

闭包的参数

闭包参数分为三种,一种是普通参数,一种是隐式参数,另一种是可变长度参数。

普通参数可以有一个可选的类型,一个参数名,还有一个可选的默认值

def closure = {int intValue = 0 -> 
println intValue 

closure() //结果:0

def closure = {intValue = 0 -> 
println intValue 

closure() //结果:0

def closure = {intValue -> 
println intValue 

closure(0) //结果:0

如果没有明确使用->来定义参数列表,那么这个闭包就定义了一个隐式的参数,这个隐式参数名叫it。

def greeting = { println "Hello ${it}"}
greeting('man')  //结果:Hello mandef greeting = {it-> println "Hello ${it}"}
greeting('man') //结果:Hello mandef greeting = {-> println "Hello ${it}"}
greeting('man') //结果:Caught: groovy.lang.MissingMethodException

最后一个闭包调用会出错,因为参数列表定义为空,所以这个时候没有默认参数it。

在闭包中也可以使用可变长度参数,示例如下

def varParamsClosure = {String... args ->args.join(' ')
}
println varParamsClosure('Hello', 'world') //结果: Hello world

委托策略

对于闭包来说,委托是一个重要的概念,你可以随时改变委托或者委托策略。

如果想要理解委托,那么我们先要解释一下委托中使用的三个重要的变量:

  • this 代表的是闭包定义所在的类的对象
  • owner 代表的是闭包定义所在的类或者闭包对象
  • delegate 代表的是第三方对象,在这个第三方对象中你可以找到闭包中未定义的方法调用或者属性

如果没有理解上面的三个概念,那没有关系,下面看看列子就知道是什么意思了。

this的使用例子

class Enclosing{void run(){def whatIsThis = {this}println whatIsThis() == this println this}
}def enclosing = new Enclosing()
enclosing.run() //结果:true, Enclosing@7f010382
println enclosing //结果:Enclosing@7f010382, 和run()中this的引用地址一致,说明就是同一个变量class ClosureInInnerClass{class Inner{def closure = {this}}void run(){def inner = new Inner()println inner.closure() == inner}
}
def closureInInnerClass = new ClosureInInnerClass()
closureInInnerClass.run() //结果:trueclass NestedClosures{void run(){def closure = {def nestedClosure = {println thisthis}println nestedClosure() == this}closure()}
}
def nestedClosures = new NestedClosures()
nestedClosures.run() //结果:NestedClosures@1534f01b, true
println nestedClosures //结果:NestedClosures@1534f01b, 跟闭包nestedClosure中的this是一致的

从这个例子中,我们可以看出this跟Java语言中概念一致,就是当前类变量,不管是在闭包里访问,还是在闭包的闭包里访问,都是一致的。

闭包的owner属性

闭包的owner就是直接包括闭包的类或者闭包对象, 也就是说如果闭包直接定义在类中,那么owner就是类对象,如果闭包直接定义在闭包中,那么owner就是闭包对象,看看例子就明白了。

class Enclosing{void run(){def closure = {owner}println this == closure()}
}def enclosing = new Enclosing()
enclosing.run() //结果:true, 这个时候因为闭包是定义在类方法里面的,也就是类里面的,所以owner等价于thisclass ClosureInInnerClass{class Inner{def closure = {owner}}void run(){def inner = new Inner()println inner.closure() == inner}
}
def closureInInnerClass = new ClosureInInnerClass()
closureInInnerClass.run() //结果:true,这个时候闭包定义在类Inner中,所以owner是inner对象class NestedClosures{void run(){def closure = {def nestedClosure = {println ownerowner}nestedClosure()}println closure() == closure}
}
def nestedClosures = new NestedClosures()
nestedClosures.run() //结果:NestedClosures$_run_closure1@65466a6a, true, 这个时候闭包nestedClosure定义在闭包closure中
println nestedClosures //结果:NestedClosures@2ea227af

闭包的delegate属性

闭包的默认delegate属性是owner,不过你可以更改delegate属性,也可以更改delegate策略。

class Enclosing{void run(){def clThis = {this}def clOwner = {owner}def clDelegate = {delegate}println clThis() == clOwner()println clOwner() == clDelegate()}
}
def enclosing = new Enclosing()
enclosing.run()// 结果:true,true

delegate的使用

class Person{String name
}
class Thing{String name
}
def person = new Person(name:'Mary')
def sayHello = {println "Hello " + delegate.name}
sayHello.delegate = person
sayHello() //结果: Hello Mary
def thing = new Thing(name:'Cat')
sayHello.delegate = thing
sayHello() //结果: Hello Cat

从这里看出,定义闭包的时候,name属性还没有定义,闭包是通过delegate的对象来获取该对象的name属性的。

delegate的策略

class Person{String namedef closure = {println "Hello " + name}
}
class Thing{String name
}def person = new Person(name:'Mary')
person.closure() //结果:Hello Mary
def thing = new Thing(name:'Cat')
person.closure.delegate = thing
person.closure() //结果:Hello Mary, 这是因为闭包的delegate策略默认是owner优先,
由于closure的owner是person这个对象,所以取的name值为Mary,
不过如果这个属性在owner不存在的话,那么会去delegate对象中去找。

请看下面这个例子

class Person{String namedef closure = {println "${name} is ${age} years old"}
}class Thing{String nameint age
}def person = new Person(name:'Mary')
def thing = new Thing(name:'Cat', age:2)
person.closure.delegate = thing
person.closure() //结果: Mary is 2 years old, 从这里可以看出name值来自person对象,
age值来自thing对象
person.closure.resolveStrategy = Closure.DELEGATE_FIRST
person.closure() //结果:Cat is 2 years old,改变闭包的委托策略,变成委托优先,
那么这个时候name和age的属性都来之thing变量

闭包的委托策略类型有如下几种:

  • Closure.OWNER_FIRST 这个是闭包的默认委托策略,如果这个属性/方法在owner中存在,那么就使用owner中的属性/方法。否则,使用delegate的属性/方法
  • Closure.DELEGATE_FIRST 解析逻辑,如果delegate中有这个属性/方法,那么使用delegate中的属性/方法。否则,使用owner中的
  • Closure.OWNER_ONLY 解析逻辑,只在owner中查看这个属性/方法,delegate中的忽略
  • Closure.DELEGATE_ONLY 解析逻辑,只在delegate中查看这个属性/方法,owner中的忽略
  • Closure.TO_SELF 自定义,比较复杂,不怎么使用

    class Person{ 
    String name 
    def closure = {“nameis{age} years old”} 
    }

    class Thing{ 
    String name 
    int age 
    }

    def person = new Person(name:’Mary’) 
    def thing = new Thing(name:’Cat’, age:2) 
    person.closure.delegate = thing 
    person.closure.resolveStrategy = Closure.OWNER_ONLY 
    person.closure() //结果: Caught: groovy.lang.MissingPropertyException: No such property: 
    age for class: Person

上面的例子虽然将thing对象委托给闭包,但是因为委托策略为OWNER_ONLY,所以,即使delegate对象中存在age属性,但是这个闭包还是不能访问。

有了这些Groovy语言基础,再去看gradle脚本,应该会感觉容易些。而且如果碰到不清楚的地方,直接去查看DSL文档就行,毕竟gradle脚本实际上就是一些API的调用。在下面的一篇文章中会简单介绍如何去查看DSL的API,以及gradle工具是如何去解析定义在build.gradle文件的。

这篇关于groovy之closure的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

idea报错Cannot compile Groovy files: no Groovy library is defined for module 'xx'之方

0、病因描写 clone 原有的项目(新建的项目一般不会有) IntelliJ IDEA 关联了后缀为groovy的文件但是没有配置Groovy的library。 1、病因一之解决方法 病因:本项目不需要用的Groovy但是关联了groovy了文件 方子:那直接去掉后缀为groovy的关联即可 结果:良好,网上基本都是这种方子 如图:去掉红框部分并apply 2、病

groovy动态脚本语言学习总结

就个人体会而言groovy是一门类似于scala的语言,集函数式与对象于一体的一门语言,从另外一个角度来看也可以说是在java基础上的封装,java能支持的操作,groovy基本都支持,毕竟groovy的运行环境也是jvm,其编译后的文件依然是.class后缀的文件,包括groovy的script编译后也是系统生成main函数去调用的,所以大部分跟java是一样的,是可以满足大部分java开发者顺

Cannot resolve type description for org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite

项目中别的jar包引用的版本为2.4.8,版本太高,直接将高版本的排除掉,然后换成2.1.7。问题解决 <dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-executor</artifactId><version>1.5.3</version><exclusions><exclusion><groupId>com.goo

Neon4.6 处理Groovy-Eclipse 插件安装不成功问题解决办法

问题: 1)org.codeehaus.groovy.eclipse.2.6.1.xx-20120301-1300-e36-RELEASE is unknown in the solver!  2)"JDT Core patch for Groovy-Eclipse plugin on Eclipse 4.5" is not applicable to the current configur

Groovy:程序员的 DSL

什么是DSL? 领域特定语言,针对一个特定的领域,具有受限表达性的一种计算机程序语言。可以看做是一种抽象处理的方式。 具有四个元素,第一个是计算机程序设计语言,使用DSL来指挥计算机做事情,语言性(一种特定的语言),受限的表达性,并不像同通用的设计语言那样具有广泛的能力,针对一个明确的领域。 分类有哪些? 外部DSL:不同于应用系统主要使用语言的语言,通常采用自定义语法,宿主应用的代码采用

深入学习-Gradle-自动化构建技术(二)Groovy-筑基

但是,如果你这个类或变量要用于其它模块的,建议不要使用 def,还是应该使用 Java 中的那种强类型定义方式,因为使用强类型的定义方式,它不能动态转换为其它类型,它能够保证外界传递进来的值一定是正确的。如果你这个变量要被外界使用,而你却使用了 def 类型来定义它,那外界需要传递给你什么才是正确的呢?这样会使调用方很疑惑。 如果此时我们在后面的代码中改变上图中 x1 的值为 String 类型

闭包表(Closure Table)

设计血缘关系(data-lineage)时,想到要使用的表模型。 表设计  节点记录表 - node CREATE TABLE `lineages_node` (`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '节点名称',`id` bigint(20) unsigned NOT NULL AUT

Eclipse集成Groovy插件

Eclipse marS.2可以直接在JAVA工程中创建groovy类进行编写 Eclipse集成Groovy插件(以eclipse-4.3.0为例) 2014-04-04 16:53 17174人阅读 评论(0) 收藏 举报 Eclipse集成Groovy插件(以eclipse-4.3.0为例) 步骤一: 下载eclipse4.3.0,地址:http://www.eclipse.org

Closure bindTo $this 与 receiver

闭包:在局部作用域(通常是函数或方法)内,引用了外部数据的函数。 Closure Closure::bindTo ( object $newthis [, mixed $newscope = 'static' ] ) 复制当前闭包对象,绑定指定的$this对象和类作用域。 newthis 绑定给匿名函数的一个对象,或者 NULL 来取消绑定。 newscope 关联到匿名函数

Jenkins高级篇之Pipeline实践篇-2-groovy中字符串操作split()和tokenize()区别

这篇来一个字符串切割的练习,很多人知道字符串切割是用split()方法,但是很少人知道在groovy中,有一个方法叫tokenize(),这两个方法其实都可以实现字符串切割,但是两者还是有区别的,本篇就来学习和掌握两者的共同点和区别。我也是,之前第一选择就是使用split()函数,直到知道有tokenize()方法, 1.split()得到结果是一个字符串数组,tokenise()得到结果是一个