Groovy(六)groovy闭包

一  闭包

自己在'Python'、'Lua'、'Go'、'Perl'中都'遇到过闭包',后续对比差异

核心: 'Lua'和'Groovy'中闭包是一个核心点

疑惑: groovy为什么要闭包?闭包的'优势'?-->仅仅是能够'访问闭包外'的属性和方法吗?

各种语言里面闭包的定义

百度百科

编程语言中的闭包

(1)定义

书面解释: 闭包是可以用作'函数参数'和'方法参数'的'代码块'

官方理解: Groovy中的闭包是一个'开放、匿名'的'代码块,'可以'接受'参数、返回值

通俗理解: Groovy的闭包更象是一个"代码块"或者"方法指针",代码在某处'被定义'然后在其后的'调用处'执行

更通俗: 闭包在groovy中是一个'处于代码上下文'中的开放的、匿名代码块,它可以'访问'到其'外部'的变量或方法

(2)粗略理解

Groovy(六)groovy闭包_第1张图片

Groovy(六)groovy闭包_第2张图片

Groovy的优点和缺点

二  闭包的实践

(1)原理理解

1)闭包就是一段可以'使用参数'的'代码片段'

2)每个'闭包'会被编译成'继承'groovy.lang.Closure 类的类-->具体查看'编译后的.class文件'

3)最后groovy编译器都会把编译成对'doCall()方法'的调用,这是groovy对闭包的一个隐藏方法,通过该方法可以'传递参数'并'调用这个闭包'

(1)创建闭包

①  无参数的闭包

++++++'变量作用域问题'++++++

1)闭包获'取了闭包外'str的值

备注:方法是不能'访问外部def' 定义的变量

2)当变量值发生变化,闭包里面变量的值'同样发生变化'

Groovy(六)groovy闭包_第3张图片

Groovy(六)groovy闭包_第4张图片

Groovy(六)groovy闭包_第5张图片

Groovy(六)groovy闭包_第6张图片

②  带参数的闭包-->'不给默认值'

备注: 多个参数,以'逗号隔开'即可

Groovy(六)groovy闭包_第7张图片

Groovy(六)groovy闭包_第8张图片

③  带参数的闭包-->设置默认值

Groovy(六)groovy闭包_第9张图片

④  Groovy几个常用的闭包

Groovy(六)groovy闭包_第10张图片

Groovy(六)groovy闭包_第11张图片

Groovy(六)groovy闭包_第12张图片

说明: 闭包一定是'有返回值'的,这就是闭包'有别于方法'的一点,方法是可以'没有返回值'的

三  闭包的一个内置变量

①  it

说明: 目前先学习'隐式'变量-->'it'

效果: 如果没有在'闭包块'中定义变量,来接收'传入参数',默认用'it'来接收

说明: 调用是如果'没有传'参数,it为'null'

Groovy(六)groovy闭包_第13张图片

源码分析 upto 方法是如何使用闭包的

说明: 没有学过'Java面向对象'的知识,可以跳过'②、③、④'部分,后续进行讲解

四  闭包的三个内置对象

①  this owner delegate

++++++++++++++++'内置对象的特点'++++++++++++++++

1)this     : 跟Java一样,是定义'闭包所在类'的一个引用,不管有'多少层闭包'嵌套,this指向的都是最上层的类

细节: 如果是'内部类'的闭包,则指向内部类

通俗: 闭包中的this就是'定义闭包的类对象'

2)owner    : 封闭闭包的对象(如果只有一层闭包就是this,如果有'多层闭包嵌套'就是含有此闭包的'上层'闭包)

通俗理解: 定义闭包的'直接外围对象'-->除了类,闭包也是'对象'

3)delegate : 缺省值是owner,但是'可以改变'

细节: 它的'主要作用'是让你在定义闭包时'访问一些你不拥有'的变量​

+++++++++++this、owner、delegate'指向总结'+++++++++++

1)this  永远是指定义'该闭包类';如果存在'内部类',则是'最内层'的类,但this'不是指当前闭包'对象

2)owenr 永远是指定义该闭包'最近的类'或者该'owner'所在闭包的'上一级闭包'对象

说明: 类似'javascript' 的原型,向上'查找最近'的

备注: 闭包只能定义在'类中'或者'闭包中'

+++++++++++闭包的 'owner' 与闭包中的 'this'区别+++++++++++

细微的区别:owner 将返回'直接封闭对象',可能是是'闭包'也可能是'类';而'this'返回的永远是'类'

代理是 groovy 闭包的关键,为 groovy '成 SDL 语言'提供基础

demo案例

1)只有一个类

说明: 在闭包中'定义'闭包

结构: 一个类中定义'闭包1',在'闭包1'内定义'闭包2',在'闭包2'定义'闭包3'
package wzj.com.groovy

class Class1 {
    def closure = {
        println "=====================分割线1====================="
        // this : 跟Java一样,是定义闭包所在类的一个引用;不管有多少层闭包嵌套,this指向的都是'最上层的类'
        println "this = " + this.class.name
        // owner : 封闭闭包的对象(如果只有一层闭包就是this;如果有'多层闭包'嵌套就是含有此闭包的'上层闭包')
        println "owner = " + owner.class.name
        // delegate :缺省值是owner
        println "delegate = " + delegate.class.name
        def nestedClos = {
            println "=====================分割线2====================="
            println "this = " + this.class.name
            println "owner = " + owner.class.name
            println "delegate = " + delegate.class.name
            def thirdClos = {
                println "=====================分割线3====================="
                println "this = " + this.class.name
                println "owner = " + owner.class.name
                println "delegate = " + delegate.class.name
            }
            thirdClos()
        }
        nestedClos()
    }
}

Groovy(六)groovy闭包_第14张图片

Groovy(六)groovy闭包_第15张图片

++++++++++++++'解读'++++++++++++++

1)只有一个类('没有内部类'),则'this'始终是该类本身-->'Class1'

2)只有一个类('没有内部类'),则'owner'是'owner当前所在闭包'的'上一级'闭包

  效果1: nestedClos闭包内所在的'owner'是上一级闭包'closure' -->也即Class$_closure1

  效果2: thirdClos闭包所在的'owner'是上一级闭包'nestedClos' -->也即Class$_closure1$_closure2

2)定义内部类

++++++++++++++'闭包结构说明'++++++++++++++

1)内部类'InnerClass'中定义闭包-->'outerClosure'

2)在内部类定义的闭包'outerClosure'内再定义闭包'innerClosure'
package wzj.com.groovy.closure

class OuterClass {

    // 说明: 在OuterClass定义一个'内部类'InnerClass
    class InnerClass {
        // 说明:在'内部类'InnerClass中定义了一个'outerClosure闭包'
        def outerClosure = {
            // 说明:在outerClosure'闭包中'定义了一个'innerClosure闭包'
            def innerClosure = {
            }
            // 4)打印innerClosure闭包对应的this,owner,delegate对象和innerClosure对象的'toString方法'
            printfMsg("innerClosure", innerClosure)
            println("------")
            // 5)打印outerClosure闭包对应的this,owner,delegate对象和outerClosure对象的'toString方法'
            printfMsg("outerClosure", outerClosure)
        }

        void printfMsg(String flag, Closure closure) {
            // 在闭包内部,有三个内置对象this、owner、delegate,我们可以直接this、owner、delegate调用
            // 1、getThisObject() 等于 'this'
            def thisObject = closure.getThisObject()
            // 2、getOwner() 等于 'owner'
            def ownerObject = closure.getOwner()
            // 3、getDelegate() 等于 'delegate'
            def delegate = closure.getDelegate()
            println("${flag} this: ${thisObject.toString()}")
            println("${flag} owner: ${ownerObject.toString()}")
            println("${flag} delegate: ${delegate.toString()}")
        }
    }

    def callInnerMethod() {
        // 2)调用内部类
        def innerClass = new InnerClass()
        // 3)调用内部类的'outerClosure闭包'
        innerClass.outerClosure.call()
        println("------")
        println("outerClosure toString ${innerClass.outerClosure.toString()}")
    }

    static void main(String[] args) {
        // 1)main入口
        new OuterClass().callInnerMethod()
    }
}

Groovy(六)groovy闭包_第16张图片

说明: 省略'@'之后的字符理解即可

+++++++++++++++'inner'Closure+++++++++++++++

1)this:结果是OuterClass$InnerClass对象

2)owner:结果是OuterClass$InnerClass$_closure1对象,即'outerClosure'

3)delegate:同owenr

+++++++++++++++'outer'Closure+++++++++++++++

1)this:结果是OuterClass$InnerClass对象

2)owner:结果是OuterClass$InnerClass对象

3)delegate:同owenr

②  重点讲解  delegate

闭包可以'设置'delegate对象,设置delegate的'意义'-->就是将'闭包'和一个'具体的对象'关联起来

delegate'委托'是一种常用'设计模式'

需求:闭包里'修改'Person类的'name'和'age',还想'调用eat方法',如何关联?

1)定义Groovy类 

package wzj.com.groovy.closure

// Person.groovy
class Person {
    def name
    def age

    def eat(String food) {
        println("你喂的${food}真难吃")
    }

    // 重写(Override)重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都'不能改变'
    @Override
    String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}'
    }
}

2)Groovy测试

package wzj.com.groovy.closure

class Main {
    def cc = {
        name = "刘徳化"
        age = 30
        // 调用eat方法
        eat "油条"
    }

    static void main(String... args) {
        Main main = new Main()
        Person person = new Person(name: "wzj", age: 18)
        println person.toString()
        // 1、修改之前:cc闭包的delegate是'Main对象'
        // 2、修改之后:cc闭包的delegate是'Person对象'
        main.cc.delegate = person
        // 2)调用cc闭包-->修改了name和age属性
        main.cc.call()
        // 3)打印修改后的
        println person.toString()
    }
}

3)测试结果

Groovy(六)groovy闭包_第17张图片

结果: 在'闭包中'可以访问'被代理对象'的属性和方法

++++++++++++++++delegate'解决'闭包内的'属性的访问'或'方法的调用'++++++++++++++++

疑问:如果'闭包所在的类'或'闭包中和被代理的类'中有'相同'名称的方法,到底要调用哪个方法,groovy考虑到了这个问题,为我们设定了'几个代理的策略':

也即: 闭包拥有的'不同的delegate策略'如下:

Groovy(六)groovy闭包_第18张图片

在闭包中,当一个'属性'或者'方法'没有'指明其所有者'的时候,delegate策略就会发挥作用了

例如: 上面的'name'、'age'属性,以及'eat'方法
//      修改策略方式

//      main.cc.setResolveStrategy(Closure.DELEGATE_FIRST)
//      main.cc.setResolveStrategy(Closure.OWNER_FIRST)

delegate策略

其它参考

③  继续讲解一个案例

//delegate委托的用法

class Dog{

    def play = {
      "wang wang!"
    }

    def childmind = {
        println    delegate.play();      
    }
}

class Cat {
    def play = {"mi mi !"}
}

def dog = new Dog()
def cat = new Cat()

// 1)修改delegate之前
dog.childmind()
// 2)修改delegate之后的
dog.childmind.delegate  = cat;
dog.childmind()

// 上面的例子是狗爸爸让老猫帮忙照看他的狗儿子玩游戏。
后续: 辩证Java8的'Lambda表达式'、'匿名内部类'、'闭包'关系

从Java到Groovy的八级进化论

搞定Groovy闭包这一篇就够了

你可能感兴趣的:(Groovy,java)