`
月迷津渡
  • 浏览: 102194 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Groovy基础——Closure(闭包)详解

 
阅读更多

这篇文章介绍一下Closure的一些语法特性,以及它的使用方法,我们用规则以及代码的方法进行展现,和介绍MetaClass不同的是,这次我们将先列出规则,再贴上代码,让大家对所阐述的目标更加明确。

 

一、代码块(Blocking)

在介绍闭包前,先来讲几个Groovy中代码块的一些特性。

1.groovy的变量作用域和java相似,代码块内部声明的变量不能被外部访问调用。

2.对于Groovy Script, 用def定义的变量对binding.variables不可见。没有def等任何定义的可被binding.variable.参数名所访问。见代码:

 

def c = 5
assert c == 5
d = 6
assert d == 6 //def keyword optional because we're within a script context
assert binding.variables.c == null
assert binding.variables.d == 6
           //when def not used, variable becomes part of binding.variables

 3.对于第一条规则,有个例外,当变量没有def等任何定义时,该变量全局有效。见代码:

 

try{
  h = 9
  assert binding.variables.h == 9
}
assert h == 9
assert binding.variables.h == 9

 4.代码块可以嵌套,比如try代码块,这和Java是一样的。

 

二、闭包(Closures)

之前对代码块做了一些总结,这里言归正传,看看Groovy的闭包是多么的强大。

1. 闭包中可以包含代码逻辑,闭包中最后一行语句,表示该闭包的返回值,不论该语句是否冠名return关键字。如果c是无参数闭包,那么它的标准调用方法是c.call(),它的简洁调用方法是c()。见代码:

 

def a = 'coffee'
def c = {
  def b = 'tea'
  a + ' and ' + b //a refers to the variable a outside the closure,
                  //and is remembered by the closure
}
assert c() == 'coffee and tea' //short for c.call()

 2.闭包赋值给一个变量,和变量与变量间的赋值一致。见代码:

 

def c
try{
  def a = 'sugar'
  c = { a } //a closure always returns its only value
}
assert c() == 'sugar'
def d = c //we can also assign the closure to another variable
assert d() == 'sugar'

 3.调用闭包的方法等于创建一个闭包实例。对于相同闭包创建出来的不同实例,他们的对象是不同的。见代码:

 

c = { def e = { 'milk' }; e }
d = c
assert c == d
v1 = c()
v2 = c()
assert v1 != v2

 

三、闭包参数

这部分需要要讲的内容是闭包参数的运用和快捷,掌握了该部分内容有助于对于闭包得心应手地运用。

1.闭包的参数声明写在‘->’符号前,调用闭包的的标准写法是:闭包名.call(闭包参数)。见代码:

 

def toTriple = {n -> n * 3}
assert toTriple.call( 5 ) == 15

 

2.对于单一存在的参数it可以不用声明,直接使用it,it在Groovy中有着特殊的意义。见代码:

 

c = { it*3 }
assert c( 'run' ) == 'runrunrun'

 

3.当且仅当闭包中有且仅有一个参数,且不显示声明,it具有唯一参数引用的作用,其他情况下,如果在闭包参数声明中没有it,那么闭包的逻辑代码块中的it降级为普通的变量。见代码:

 

//c = { def it = 789 }
          //a compile error when uncommented: 'it' already implicitly defined
c = { value1 -> def it = 789; [value1, it] }
          //works OK because no 'it' among parameters
assert c( 456 ) == [456, 789]
c = {-> def it = 789; it } //zero parameters, not even 'it', so works OK
assert c() == 789
 

 

4.闭包中的参数名不能重复,it除外。见代码:

 

def name= 'cup'
//def c={ name-> println (name) } //a compile error when uncommented:
                                  //current scope already contains name 'name'
c= { def d= { 2 * it }; 3 * d(it) }
      //'it' refers to immediately-surrounding closure's parameter in each case
assert c(5) == 30

 

5.如果在脚本范围内Scope已经有it的定义声明,如果闭包中再使用it特性,那脚本中的it就近表示闭包中的参数,而owner.it表示脚本范围的it参数。这个和java中的this有几分相似。见代码:

 

it= 2
c= { assert it == 3; assert owner.it == 2 }
c(3) 

6. 我们可以将闭包作为参数传入另外一个闭包,同时可以从一个闭包返回一个闭包。

 

toTriple = {n -> n * 3}
runTwice = { a, c -> c( c(a) )}
assert runTwice( 5, toTriple ) == 45

def times= { x -> { y -> x * y }}
assert times(3)(4) == 12

 7.闭包的一些快捷写法,当闭包作为闭包或方法的最后一个参数。可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略。对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略。见代码:

 

def runTwice = { a, c -> c(c(a)) }
assert runTwice( 5, {it * 3} ) == 45 //usual syntax
assert runTwice( 5 ){it * 3} == 45
    //when closure is last param, can put it after the param list

def runTwiceAndConcat = { c -> c() + c() }
assert runTwiceAndConcat( { 'plate' } ) == 'plateplate' //usual syntax
assert runTwiceAndConcat(){ 'bowl' } == 'bowlbowl' //shortcut form
assert runTwiceAndConcat{ 'mug' } == 'mugmug'
    //can skip parens altogether if closure is only param

def runTwoClosures = { a, c1, c2 -> c1(c2(a)) }
    //when more than one closure as last params
assert runTwoClosures( 5, {it*3}, {it*4} ) == 60 //usual syntax
assert runTwoClosures( 5 ){it*3}{it*4} == 60 //shortcut form

8.闭包接受参数的规则,会将参数列表中所有有键值关系的参数,作为一个map组装,传入闭包作为调用闭包的第一个参数。见代码:

 

def f= {m, i, j-> i + j + m.x + m.y }
assert f(6, x:4, y:3, 7) == 20

def g= {m, i, j, k, c-> c(i + j + k, m.x + m.y) }
assert g(y:5, 1, 2, x:6, 3){a,b-> a * b } == 66

9.闭包提供了询问自己参数个数的方法,无论在闭包内或者闭包外。见代码:

 

c= {x,y,z-> getMaximumNumberOfParameters() }
assert c.getMaximumNumberOfParameters() == 3
assert c(4,5,6) == 3

10.闭包可以将其最后的参数设置其默认的取值。见代码:

 

def e = { a, b, c=3, d='a' -> "${a+b+c}$d" }
assert e( 7, 4 ) == '14a'
assert e( 9, 8, 7 ) == '24a' //override default value of 'c'

11.闭包可以通过定义最后一个参数声明为Object[],来获取任意多个参数。同时,在闭包的逻辑处理中要使用这些参数则需要使用数组的each方法。

 

def c = { arg, Object[] extras ->
  def list= []
  list<< arg
  extras.each{ list<< it }
  list
}
assert c( 1 )          == [ 1 ]
assert c( 1, 2 )       == [ 1, 2 ]
assert c( 1, 2, 3 )    == [ 1, 2, 3 ]
assert c( 1, 2, 3, 4 ) == [ 1, 2, 3, 4 ]

12.如果闭包的参数声明中没有list,那么传入参数可以设置为list,里面的参数将分别传入闭包参数。见代码:

 

def c= {a, b, c-> a + b + c}
def list=[1,2,3]
assert c(list) == 6

13.闭包有一个curry方法,该方法的作用是锁定闭包的首个参数。类似于java中的方法重载。见代码:

 

def concat = { p1, p2, p3 -> "$p1 $p2 $p3" }
def concatAfterFly = concat.curry( 'fly' )
assert concatAfterFly( 'drive', 'cycle' ) == 'fly drive cycle'
def concatAfterFlySwim = concatAfterFly.curry( 'swim' )
assert concatAfterFlySwim( 'walk' ) == 'fly swim walk'

14.闭包是可嵌套的。见代码:

 

def gcd //predefine closure name
gcd={ m,n-> m%n==0? n: gcd(n,m%n) }
assert gcd( 28, 35 ) == 7

15.可以在闭包中用call闭包进行迭代。见代码:

 

def results = [];
{ a, b ->
  results << a
  a<10 && call(b, a+b)
}(1,1)
assert results == [1, 1, 2, 3, 5, 8, 13]  // Fibonacci numbers
 

以上就是所有闭包需要掌握的基本语法。

分享到:
评论
3 楼 u010753172 2018-08-13  
fruwei 写道
调用闭包的方法等于创建一个闭包实例。对于相同闭包创建出来的不同实例,他们的对象是不同的。
有点疑问
其实不一样的主要是因为
c = { def e = { 'milk' }; e }   
c()每次都反回的e是不同的,而不是相同闭包创建的闭包实例不同
比如例子:
c = { def e ='milk';e}
v1 = c()
v2 = c()
assert v1 == v2

重点是‘调用闭包的方法等于创建一个闭包实例’这句话表述有问题,博主要是先放出下面的例子,再以此作为解释就好理解一点了。

调用闭包cA就是执行cA的闭包体,本身根本就没有会创建新的闭包实例的说法,不知道的还以为每次调用闭包就会创建一个什么闭包实例,这很容易误导别人。
而如果cA的闭包体中声明定义了某个闭包变量cB,那么每次调用闭包cA时就会重新声明和定义一个新的cB(闭包实例),每次声明定义的cB指向的都不是同一个闭包实例。这其实换成其他类型的变量也一样,创建出来的一样是不同的对象(字符串常量和其他基础数据类型缓存范围内的值等除外),跟是不是闭包没有必然联系。

因此博主的这条总结其实意义不大甚至可能会误导别人……当然其它都很精彩!
2 楼 fruwei 2017-03-23  
调用闭包的方法等于创建一个闭包实例。对于相同闭包创建出来的不同实例,他们的对象是不同的。
有点疑问
其实不一样的主要是因为
c = { def e = { 'milk' }; e }   
c()每次都反回的e是不同的,而不是相同闭包创建的闭包实例不同
比如例子:
c = { def e ='milk';e}
v1 = c()
v2 = c()
assert v1 == v2
1 楼 aplixy 2016-05-14  
讲的很详细,谢谢分享

相关推荐

    closure闭包1

    - 在Groovy中,闭包是 `groovy.lang.Closure` 类的实例,这意味着你可以将闭包赋值给变量,作为参数传递,或者作为其他对象的字段。例如: ```groovy def myClosure = { println 'Hello, World!' } ``` 3. **...

    Groovy轻松入门——Grails实战基础篇

    ### Groovy轻松入门——Grails实战基础篇 #### 搭建Grails环境及创建Grails Demo程序 **Groovy**是一种面向对象的编程语言,它运行于Java平台上,能够与Java代码无缝集成。而**Grails**则是一款基于Groovy的高性能...

    groovy(10)-闭包委托策略1

    在Groovy语言中,闭包是一种强大的特性,它允许我们创建可执行的代码块,并可以在不同的上下文中使用。闭包有三个重要的变量:`this`, `owner`, 和 `delegate`,它们各自扮演着不同的角色。 1. **this**: `this`...

    groovy基础语法.pdf

    这些知识点是对Groovy基础语法的一个大致概述,而文件中的内容则展示了如何在实际代码中应用这些语法。由于文件内容有些混乱,并且含有重复的词汇和一些扫描错误,以上总结的知识点基于对Groovy语言的理解和部分可...

    groovy基础教程源码,很全面

    Groovy基础教程源码涵盖了Groovy语言的各个方面,是学习和理解Groovy语言的宝贵资源。下面将详细阐述Groovy的一些核心知识点。 1. **动态类型与静态类型**: Groovy支持动态类型,这意味着变量在声明时无需指定...

    groovy基础语法.doc

    Groovy 基础语法 Groovy 是一种基于 Java 平台的高级编程语言,它融合了 Ruby、Python 和 Smalltalk 的一些最有用的功能,同时保留了基于 Java 语言的核心语法。Groovy 提供了更简单的替代语言,且几乎不需要学习...

    Android Studio 中运行 groovy 程序的方法图文详解

    "Android Studio 中运行 Groovy 程序的方法图文详解" Android Studio 中运行 Groovy 程序的方法可以分为以下几个步骤: 1. 新建一个 Java Library 模块 2. 修改该模块下的 build.gradle 文件,添加 Groovy 插件和...

    Show Your ToolBox——锋利的groovy

    《Show Your ToolBox——锋利的Groovy》 在IT领域,工具的选择和使用往往直接影响到工作效率和项目质量。本文将深入探讨Groovy这门强大的动态编程语言,它以其灵活性和与Java的紧密集成,成为了许多开发者的得力...

    [Groovy入门]第五讲.将流程控制语句与方法重构为闭包

    在学习Groovy的过程中,掌握闭包这一核心概念至关重要,尤其是在重构流程控制语句和方法时。本讲我们将深入探讨如何将传统的流程控制结构和方法转换为Groovy的闭包,以提高代码的可读性和可维护性。 首先,让我们...

    Z2-Groovy in Action.pdf

    Groovy是一种基于JVM(Java...Groovy的集成方式、开发环境支持、语言基础和高级特性如闭包和集合类型,共同构成了Groovy语言强大的开发能力。对于熟悉Java的开发者而言,Groovy提供了一条快速进入动态语言世界的捷径。

    Groovy need not rails——介绍自己写的一个基于groovy的框架,Webx

    Groovy是一种动态、灵活的编程语言,它设计用于Java虚拟机(JVM)上运行,并且与Java代码无缝集成。...由于与Java平台的紧密集成,Webx还能够利用丰富的Java生态系统,为大型企业级应用提供坚实的基础。

    最新 groovy开发包

    Groovy的闭包(Closure)是其核心特性之一,类似于其他语言中的lambda表达式,可以用来处理函数式编程场景。 2. **Groovy与Java的互操作性**:Groovy可以无缝地与Java代码集成,因为它们都运行在JVM上。你可以直接...

    Groovy Script 入门

    ### Groovy Script 入门知识点详解 #### 一、Groovy脚本简介 Groovy是一种灵活的面向对象的编程语言,它运行在Java平台上。由于其语法简洁且与Java高度兼容,因此对于Java开发者来说非常容易上手。Groovy不仅支持...

    groovy入门实例代码详细(包括字符串,map,闭包等等)

    通过这些实例代码,你可以逐步了解和掌握Groovy的基础知识,并将其应用到实际的项目开发中。记得动手实践,因为编程的学习离不开实践和调试。希望这个代码包能帮助你快速入门Groovy,开启编程之旅。

    groovy in action 中文版 2017.11

    Groovy提供了大量的动态特性,比如动态类型、闭包、元编程能力等,使得编写脚本或应用程序变得更加高效和愉悦。Groovy是完全兼容Java的,这意味着Java开发人员可以轻松地使用Groovy编写程序,并利用Groovy提供的强大...

    Groovy学习资料

    此外,Groovy还有闭包(Closure)的概念,这是Groovy强大的功能之一,它可以用来处理函数式编程任务,如数据过滤和映射。 其次,Groovy的动态特性是另一个关键知识点。Groovy可以在运行时改变类的结构,这意味着你...

    groovy快速入门指南(中文)

    ### Groovy 快速入门指南知识点详解 #### 一、集合操作 Groovy 提供了对集合的强大支持,包括 `List` 和 `Map` 的多种操作方式。 **1. List** - **定义与访问** - Groovy 中的 `List` 可以包含不同类型的元素。...

Global site tag (gtag.js) - Google Analytics