二、Groovy语法(二):闭包

2019-01-18  本文已影响0人  ywy_袁滚滚

Groovy闭包

1、Groovy中闭包基础

//定义一个闭包(闭包是一些代码组成的代码块对象,用{}括起来的一段代码)
def closure = { println 'Hello groovy!'}
//调用(类似定义了一个方法,然后可以去调用这个方法,可与方法对比着来理解闭包)
closure.call() //Hello groovy!
closure() //Hello groovy!
//定义一个有参数的闭包  利用->区分参数和具体执行代码
def closure = { String name -> println "Hello $name!"}
//调用
closure('groovy')  //Hello groovy!

//多个参数由逗号隔开
def closure2 = { String name,int age -> println "Hello $name! My age is $age"}
//调用
closure2('groovy',6) //Hello groovy! My age is 6

//当没有声明参数时,每个闭包都会有一个默认的参数it指代传入的参数
//只有一个参数时可考虑使用该方式声明闭包
//注意若声明了有一个参数,该闭包就没有这个it参数了!!
def closure3 = {println it}
closure3('hello groovy!') //hello groovy!
closure3([1,2,3]) //[1, 2, 3]
//闭包的返回值
def closure = { String name -> return "Hello $name!"}

def result = closure('groovy')

println result //Hello groovy! 返回值就是return 的内容

def closure1 = { println it}

def result1 = closure1("groovy")

println result1 //null ,所有的闭包都有返回值,若没有写返回,则返回null

2、Groovy中闭包使用(常用的四种)

//只列出几项,更多的方法到DefaultGroovyMethods类中查看

def x = fab(5)
def x2 = fab2(5)
println x //120
println x2 //120
/**
 * 求指定num的阶乘
 */
int fab(int number) {
    def result = 1
    //从1开始,依次递增到number,每次递增的结果传入闭包进行处理
    1.upto(number, { num -> result *= num })
    return result
}

/*
  上面的是什么意思呢? num就是递增变化的那个参数 它从1~number
  第一步,result = 1; num=1; 传入执行 result = result*num  ==> result = 1*1 = 1
  第二步,result = 1; num=2; 传入执行 result = result*num  ==> result = 1*2 = 2
  第三步,result = 2; num=3; 传入执行 result = result*num  ==> result = 2*3 = 6
 第四步,result = 6; num=4; 传入执行 result = result*num  ==> result = 6*4 = 24
 第五步,result = 24; num=5; 传入执行 result = result*num  ==> result = 24*5 = 120
*/

/**
 *求指定num的阶乘
 */
int fab2(int number) {
    def result = 1
    //从number开始,依次递减到1,每次递减的结果传入闭包进行处理
     number.downto(1){ num -> result *= num }
    return result
}

/*
   同样的 num就是递减变化的那个参数 它从number~1
  第一步,result = 1; num=5; 传入执行 result = result*num  ==> result = 1*5 = 5
  第二步,result = 5; num=4; 传入执行 result = result*num  ==> result = 5*4 = 20
  第三步,result = 20; num=3; 传入执行 result = result*num  ==> result = 20*3 = 60
 第四步,result = 60; num=2; 传入执行 result = result*num  ==> result = 60*2 = 120
 第五步,result = 120; num=1; 传入执行 result = result*num  ==> result = 120*1 = 120
*/

//注意看fab2()方法中downto()方法的写法,当方法中的最后一个参数是闭包时,可以将闭包写在括号的外面,若该方法仅有一个闭包参数,除了可以将闭包写在外面,还可以将括号省略,如下所示

/**
 * 累加 从0累加到(number-1)
 * times方法始终从0开始到number-1循环
 */
int accumulate(int number) {
    def result = 0
    number.times { num -> result += num }
    return result
}

def sum = accumulate(5)
println sum //0+1+2+3+4 = 10

//相信大家都可以理解上面的运行原理,可以自己按照上面的方式先推一遍,以便更好的理解闭包的使用
/*
   同样的 num就是递增变化的那个参数 它从number~1
  第一步,result = 0; num=0; 传入执行 result = result+num  ==> result = 0+0 = 0
  第二步,result = 0; num=1; 传入执行 result = result+num  ==> result = 0+1 = 1
  第三步,result = 1; num=2; 传入执行 result = result+num  ==> result = 1+2 = 3
 第四步,result = 3; num=3; 传入执行 result = result+num  ==> result = 3+3 = 6
 第五步,result = 6; num=4; 传入执行 result = result+num  ==> result = 6+4= 10
*/
def str = "the 2 add 3 is 5"

//each方法遍历str的每一个字符,都执行闭包中的操作,并返回它自己。每个字符都输出两遍
str.each { print it.multiply(2)} //tthhee  22  aadddd  33  iiss  55

//find方法查找符合条件的第一个,找到即停止并返回,没找到返回null
println str.find {String s -> return s.isNumber() } //2

//findAll方法查找所有符合条件的内容,返回所有符合条件的内容的集合
def listResult = str.findAll { s -> s.isNumber() }
println listResult.toListString() //[2, 3, 5] ,集合都可以转为listString的形式

//注意find和findAll方法闭包的返回值都需要是Boolean的,注意看findAll方法,闭包中的最后表达式有值时,即作为闭包的返回值,可省略return,参数类型可推导,省略

//any判断是否有符合条件的内容,有一个,即返回true,否则返回false
println str.any { s -> s.isNumber() } //true

//every方法判断是否所有的内容都符合条件,是返回true,否则返回false
println str.every { s -> s.isNumber() } //false

//collect方法对每一个元素都执行闭包中的操作,并将每一个操作过后的结果添加到一个新的ArrayList中
def list2 = str.collect { it.toUpperCase() }
println list2 //[T, H, E,  , 2,  , A, D, D,  , 3,  , I, S,  , 5]

//题外话,对list的collect操作
//对list的collect操作,去除所有的空格并且转换为大写
println str.findAll { 
String s -> !s.isBlank()}.collect { it.toString().toUpperCase() } // [T, H, E, 2, A, D, D, 3, I, S, 5]
这部分内容放到讲解Groovy中的数据结构中

3、Groovy中闭包进阶

this: 代表定义闭包处的类

owner: 代表闭包定义处的类或者对象

delegeta: 可代表做任意的对象,默认与owner一致,可手动指定

/**
 * 闭包中三个重要的变量:this,owner,delegate
 */
def scriptClosure = {
    println "scriptClosure this:" + this 
    println "scriptClosure owner:" + owner 
    println "scriptClosure delegate:" + delegate 
}
scriptClosure.call()

/*
 * 输出结果
 */
//scriptClosure this:variable.ClosureStudy@3232a28a
//scriptClosure owner:variable.ClosureStudy@3232a28a
//scriptClosure delegate:variable.ClosureStudy@3232a28a

//可以看到它们都指向了ClosureStudy类对象(即定义它们的类或者说距离最近的那个封闭类)

//ps:ClosureStudy.groovy在编译后会在out目录下生成一个继承Script的java类(脚本文件)
//========对指向最近的内部类进一步说明============

//在ClosureStudy类中再定义了一个内部类
class InnerClass {
    //定义了一个静态闭包
    def static classClosure = {
        println "classClosure this:" + this
        println "classClosure owner:" + owner
        println "classClosure delegate:" + delegate
    }

    //定义了一个静态方法
    def static mTestMethod() {
        //在方法中定义一个闭包
        def methodClosure = {
            println "methodClosure this:" + this
            println "methodClosure owner:" + owner
            println "methodClosure delegate:" + delegate
        }
        //调用
        methodClosure.call()
    }
}

//调用闭包和方法
def innerClass = new InnerClass()
innerClass.classClosure.call()
innerClass.mTestMethod()

/*
*输出结果
*/
//classClosure this:class variable.InnerClass
//classClosure owner:class variable.InnerClass
//classClosure delegate:class variable.InnerClass
//methodClosure this:class variable.InnerClass
//methodClosure owner:class variable.InnerClass
//methodClosure delegate:class variable.InnerClass

//可以看到它们都指向了定义它们的那个类的字节码,注意因为是静态的,所以指向的都是字节码(结果后面没有@xxx)。

/*
 *去掉static关键字后运行结果
 */
//classClosure this:class variable.InnerClass@6cd28fa7
//classClosure owner:class variable.InnerClass@6cd28fa7
//classClosure delegate:class variable.InnerClass@6cd28fa7
//methodClosure this:class variable.InnerClass@6cd28fa7
//methodClosure owner:class variable.InnerClass@6cd28fa7
//methodClosure delegate:class variable.InnerClass@6cd28fa7
//ClosureStudy中定义一个闭包
def nestClosure = {
    //在闭包中再定义一个闭包
    def innerClosure = {
        println "innerClosure this:" + this
        println "innerClosure owner:" + owner
        println "innerClosure delegate:" + delegate
    }
    innerClosure.call()
}
nestClosure.call()

/*
*输出结果
*/
//innerClosure this:variable.ClosureStudy@6cd28fa7
//innerClosure owner:variable.ClosureStudy$_run_closure3@4fb3ee4e
//innerClosure delegate:variable.ClosureStudy$_run_closure3@4fb3ee4e

//==========delegate的指定===========

class TestChangeDelegateClass{

}
def testChangeDelegateClass = new TestChangeDelegateClass()

//定义一个闭包
def nestClosure = {
    //在闭包中再定义一个闭包
    def innerClosure = {
        println "innerClosure this:" + this
        println "innerClosure owner:" + owner
        println "innerClosure delegate:" + delegate
    }
    innerClosure.delegate = testChangeDelegateClass //修改默认的delegate
    innerClosure.call()
}
nestClosure.call()

/*
*输出结果
*/
//innerClosure this:variable.ClosureStudy@57cf54e1
//innerClosure owner:variable.ClosureStudy$_run_closure3@17497425
//innerClosure delegate:variable.TestChangeDelegateClass@f0da945

//可以看到delegate变为我们指定的TestChangeDelegateClass对象。this和owner是定义的时候就确定了的,无法再次修改

总结:

  1. this指向定义闭包处的类,在定义的时候就确定,无法再次修改
  2. owner在闭包定义在类与方法里的时候指向定义它的类(最近的一个),而闭包被定义在闭包中时,该闭包的owner指向定义该闭包的那个闭包对象,同样的在定义的时候就确定,无法再次修改
  3. delegate默认指向和woner一致,当人为的修改后,delegate指向修改后的那个对象
class Student {
    def name
    def self_introduction = { "My name is $name" }

    @Override
    String toString() {
        return self_introduction.call()
    }
}

class Teacher {
    def name
}
//在初始化的时候传入值(先知道可以这样传,在Groovy面向对象中会讲解)
def stu = new Student(name: 'groovy')
def tea = new Teacher(name: 'java')
println stu.toString()  //输出My name is groovy

//上面的输出结果毫无疑问,那么有没有什么办法不修改学生的name的情况下让输出的name不是学生的,而是老师的name呢?

//首先闭包self_introduction中的name肯定是指向定义它的类student的name,前面说过闭包的delegate是可以修改的,我们修改闭包self_introduction的delegate指向Teacher,会怎么样呢?

//修改之后再打印
stu.self_introduction.delegate = tea
println stu.toString() //My name is groovy

//发现并没有起作用,还是输出了学生的名字。这个时候就需要闭包的委托策略了

//指定闭包的委托策略为Closure.DELEGATE_FIRST
stu.self_introduction.resolveStrategy = Closure.DELEGATE_FIRST
println stu.toString() //My name is java

//每个闭包都有自己的委托策略,默认是Closure.OWNER_FIRST,表明闭包中的变量或是方法都是先从闭包指向的owner中寻找

//定义一个teacher2,属性为name1
class Teacher2 {
    def name1
}
def tea2 = new Teacher2(name1: 'java')
//修改委托策略
stu.self_introduction.resolveStrategy = Closure.DELEGATE_FIRST
//指定delegate为tea2
stu.self_introduction.delegate = tea2
println stu.toString() //My name is groovy

//此时由于在tea2中没有找到name属性,所以又会从owner中寻找,因此输出的是groovy

//四种委托策略:Closure.DELEGATE_FIRST,Closure.OWNER_FIRST,另外还有Closure.DELEGATE_ONLY,Closure.OWNER_ONLY,从名字也可猜出各自的作用。

上一篇下一篇

猜你喜欢

热点阅读