Groovy闭包

# 闭包

  • 闭包的基础知识
  • 闭包的使用
  • 闭包 this,owner,delegate 的理解
  • 总结

## 闭包的基础知识

闭包就是一段可以使用参数的代码片段,每个闭包会被编译成继承groovy.lang.Closure 类的类(具体查看编译后的.class文件即可),这个类有一个叫 call 方法,通过该方法可以传递参数并调用这个闭包。

### 定义一个闭包

闭包定义的格式

//方式1:定义参数
{闭包参数,闭包参数,闭包参数...->闭包体}

//方式2:无定义参数,${it] 为默认的参数
{闭包体}

指定闭包参数

def closure = {String name,int age->
    println "name is ${name} and the age is ${age}"
}

不指定闭包参数

def closure2 = {
    println "the param is ${it}"
}

闭包就是一个对象

//指定闭包的类型
Closure<Boolean> isImageFile = {
   String filePath ->
        return filePath.endsWith('.png') || filePath.endsWith('.jpg')
}

println isImageFile("xxxx.png")//true

### 闭包的返回值

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

def closuer = {String name,int age->
    println "name is ${name} and the age is ${age}"
}
//因为没有调用 return ,因此返回 null
println closuer('六号表哥',26)//null

### 闭包的参数

  • 闭包的参数是可选的;
  • 有一个默认的参数;
  • 闭包多个参数使用 , 隔开;
  • 参数的类型是可选的;
  • 闭包参数接受可变参数
//不指定参数类型,多个参数用,个隔开
def clouser = {  name,  age ->
    println "name is ${name} and the age is ${age}"
}

//不指定闭包参数
def clouser = { 
    println "closure"
}

//闭包 it 为暗指参数
def clouser2 = {
    println "the param is ${it}"
}


//可变参数
def concat = { String... params ->
     params.join()
}

println concat.call("1","2","3")//123

### 调用闭包

在 Groovy 中调用闭包有两种方式

  • closuer.call(参数)
  • closuer()
def closuer = {String name,int age->
    println "name is ${name} and the age is ${age}"
}
//调用方式1
closuer.call('六号表哥',26)//name is 六号表哥 and the age is 26
//调用方式2
closuer('六号表哥',26)//name is 六号表哥 and the age is 26

//使用 ${it} 表示默认参数
def closuer2 = {
    println "the param is ${it}"
}

closuer2('使用默认参数')//the param is 使用默认参数

## 闭包的使用

  • 闭包与基本数据类型的集合
def result = 0;
//从10递增到20,累加每一个值
10.upto(20) {

    int value ->
        result += value
}

println result

  • 闭包与String字符串的结合
String name = "Hello Groovy"

//name.each {
//    String str ->
//        print str + " "//H e l l o   G r o o v y
//}

//查找第一个符合条件的值
println name.find {
    String str ->
        if (str.equals("o")) {
            return str
        }
        return null

}

println name.findAll {
    if (it.equals("o")) {
        return it;
    }
    return null
}.toListString()//[o, o, o]


String book = "20180701"
println book.every {
    String str ->
        return str.isInteger()//true
}

println name.any {
    return it.equals("o")//true
}


String str = "name:liuhaobiaoge,age:26,level:middle,1:update,0:canel"
str.findAll {
    if (it.isInteger()) {
        return it;
    }
    return null
}.collect {
    String value ->
        print value.toInteger() * 2 + "  "//4  12  2  0
}
  • 闭包与结合数据结构集合
def list = [1, 2, 3, 4]

println list.every { it ->
    return it > 0
}
  • 闭包与文件结合

TODO

### 通过源码分析 upto 方法是如何使用闭包的?

upto 方法它的职责很单一就是一个递增的功能,但是每递增一次,对应的需要给外界知道,那么就是通过执行一段闭包,将这个值暴露出去,这种设计很好,它不会影响 upto 方法内部的执行逻辑。

  • 调用 uptp 方法,传入闭包
def result = 0;
//从10递增到20,累加每一个值
10.upto(20) {

    int value ->
        result += value
}

println result
  • 进入 upto 源码

代码很简单,具体每一行代码都加了注释

/**
 * Iterates from this number up to the given number, inclusive,
 * incrementing by one each time.
 *
 * @param self    a Number
 * @param to      another Number to go up to
 * @param closure the closure to call
 * @since 1.0
 */
public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
    //self的值就是10
    int self1 = self.intValue();
    //to1的值就是20
    int to1 = to.intValue();
    //满足条件
    if (self1 <= to1) {
        //从10遍历到20
        for (int i = self1; i <= to1; i++) {
            //每一个值传递给我们先前定义的闭包closure,并执行该闭包。
            closure.call(i);
        }
    } else
       ...
}

## this,delegate,ower 的区别

### 定义

  • this 表示定义闭包的类

  • owner 表示定义闭包的类或者闭包(闭包内还是可以定义闭包的)

  • delegate 默认与 owner 一致,可以手动修改 delegate 的值

### 区别

  • 一般情况下这三者是一样的。
  • 闭包中定义在闭包中,thisowerdelegate不一致。
  • 如果修改 delegate ,那么 owerdelegate 就不一致。

默认情况下 thisowner,delegate 是一样的。

class Enclosing {

    void run() {
        def closure = {
            println this//Enclosing@69997e9d
            println owner//Enclosing@69997e9d
            println delegate//Enclosing@69997e9d
        }

        closure()
    }
}

闭包中的this就是定义闭包的类对象

验证1:测试闭包的 this 和定义闭包的类的 this 是否一致

class Enclosing {

    void run() {
        def whatIsThisObject = { getThisObject() }

        assert whatIsThisObject() == this

        println whatIsThisObject()//Enclosing@3514a4c0
        
        //this就是Enclosing对象
        println this//Enclosing@3514a4c0

        def whatIsThis = { this }
        //返回闭包的this对象
        println whatIsThis()//Enclosing@3514a4c0
    }
}

new Enclosing().run()

验证2:使用内部类来验证闭包的this和定义闭包的类的this是一样的

class EnclosdInnerClass {
    class Inner {
        //这里返回的this就是Inner对象
        def closure = { this }
    }

    void run() {
        def inner = new Inner()
        
        println inner.closure()//EnclosdInnerClass$Inner@6babf3bf
        //打印内部类对象
        println inner//EnclosdInnerClass$Inner@6babf3bf
        //这个是外部类对象
        println this//EnclosdInnerClass@3059cbc
    }
}

new EnclosdInnerClass().run()

验证3:验证闭包的this和定义闭包的类的this是一样的


class Person {

    String name
    int age

    String toString() {
        "${name} is $age year old"
    }


    String dump() {
        def closure = {
            //this 表示就是定义closure 闭包的类,这个类就是Person
            //因此这里调用的toString就是Person的toString()方法
            def msg = this.toString()
            println msg
            return msg
        }
        closure()
    }
}
//如果不知道构造参数,那么在传参时就要执行参数的名称
Person p = new Person(name: "六号表哥", age: 26)
p.dump()//六号表哥 is 26 year old

闭包中的 owner 就是定义闭包的闭包或者类对象

验证1:

class OwnerEnclosing {
    void run() {
        def whatIsOwnerMethod = {
            getOwner()
        }
        //返回闭包的owner对象
        println whatIsOwnerMethod()//OwnerEnclosing@6e0f5f7f
        //返回定义闭包类的对象
        println this//OwnerEnclosing@6e0f5f7f

        def whatIsOwner = { owner }
        println whatIsOwner()//OwnerEnclosing@6e0f5f7f

    }
}

OwnerEnclosing ownerEnclosing = new OwnerEnclosing()
ownerEnclosing.run()

验证2:

class OwnerEnclosdInnerClass {
    class Inner {
        def closure = { owner }
    }

    void run() {
        def inner = new Inner()
        //返回 closure 这个闭包的owner对象
        println inner.closure()//OwnerEnclosdInnerClass$Inner@53ce1329
        //返回内部类,也就是定义闭包的类对象
        println inner//OwnerEnclosdInnerClass$Inner@53ce1329
    }
}

new OwnerEnclosdInnerClass().run()

验证3:

class NestedClosure {
    void run() {
        def closure = {

            def cls = { owner }
            //返回的cls闭包的owner,也就是closure闭包对象
            cls()
        }
        
        println closure()//NestedClosure$_run_closure1@32502377
        println closure//NestedClosure$_run_closure1@32502377

    }

}

new NestedClosure().run()

闭包中的 deleagate 一般情况下和 owner 一致的,但是可以修改

class DelegateEnclosing {
    void run() {

        def cls1 = { getDelegate() }

        def cls2 = { delegate }

        println cls1()//DelegateEnclosing@7bab3f1a
        println cls2()//DelegateEnclosing@7bab3f1a
        println this//DelegateEnclosing@7bab3f1a

        def enclosed = {
            //这里返回的是定义闭包的闭包对象,也就是enclosed闭包对象
            { -> delegate }.call()
        }

        println enclosed()//DelegateEnclosing$_run_closure3@12aba8be
        println enclosed//DelegateEnclosing$_run_closure3@12aba8be

    }
}

new DelegateEnclosing().run()

验证:闭包的delegate是可以修改的

class P {
    String name
}

class Q {
    String name
}


P pp = new P(name: "pp")
Q qq = new Q(name: "qq")

def closure = {
    delegate.name.toUpperCase()
}

//将闭包的deleagte指向pp
closure.delegate = pp

println closure()//PP

closure.delegate = qq
println closure()//QQ

## 总结

在本节中,简单的总结了闭包的相关概念,定义,使用,以及this,owner,delegate几个关键字的理解。闭包在 Groovy 中应用很广泛,本节将闭包的应用分为与基本数据类型结合的闭包,与String 字符串结合的闭包,与数据结构结合的闭包,与文件结合的闭包(还没学习,不知道怎么耍,TODO),仅仅通过上面所总结的还是不够的,接下来还要好好深入学习其 Closure 相关的知识点。

「记录于2018-07-01晚」

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容