美文网首页
二、Groovy语法(二):闭包

二、Groovy语法(二):闭包

作者: ywy_袁滚滚 | 来源:发表于2019-01-18 10:37 被阅读0次

    Groovy闭包

    1、Groovy中闭包基础

    • 1.1 闭包的概念
      闭包是被包装成对象的代码块,可以通过一个变量引用到它,也可以传递给别人进行处理(像处理一个对象一样处理闭包,比如作为参数传递、作为一个方法的返回值等)

    • 1.2 闭包的定义和调用

    //定义一个闭包(闭包是一些代码组成的代码块对象,用{}括起来的一段代码)
    def closure = { println 'Hello groovy!'}
    //调用(类似定义了一个方法,然后可以去调用这个方法,可与方法对比着来理解闭包)
    closure.call() //Hello groovy!
    closure() //Hello groovy!
    
    • 1.3闭包参数(普通参数和隐式参数)
    //定义一个有参数的闭包  利用->区分参数和具体执行代码
    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]
    
    • 1.4 闭包的返回值
    //闭包的返回值
    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中闭包使用(常用的四种)

    • 2.1 与基本类型的结合使用
    //只列出几项,更多的方法到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
    */
    
    • 经过上面的推导之后,相信对于闭包的使用已经问题不大了,只要知道了与之结合使用的方法的运作方式,即可慢慢理解

    • 2.2 与String的结合使用

    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]
    
    • 2.3 与数据结构的结合使用
    这部分内容放到讲解Groovy中的数据结构中
    

    3、Groovy中闭包进阶

    • 3.1、闭包的关键变量(this,owner,delegeta

    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
    
    • 说了这么多,可以看到this,owner,delegete都是一样的值,那么对于owner的说明中,“指代定义闭包处的类或者对象”,指代“对象”又是怎么一回事呢?
    //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
    
    
    • 可以看到,this依然指向定义它的ClosureStudy类,但是owner和delegate却指向了ClosureStudy中的对象_run_closure3@4fb3ee4e。还记得闭包的概念吗?闭包是被包装成对象的代码块,闭包就是一个对象(Closure类型的对象),所以在闭包中定义的闭包的owner实际上指向了定义它的那个闭包对象,而delegate的指向默认与owner一致,所以他也指向了定义闭包的那个闭包对象。下面再看一下delegate的指定。
    //==========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指向修改后的那个对象
    • 3.2、闭包的委托策略(this,owner,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,从名字也可猜出各自的作用。
    
    

    相关文章

      网友评论

          本文标题:二、Groovy语法(二):闭包

          本文链接:https://www.haomeiwen.com/subject/mpvgdqtx.html