Groovy语法之闭包

本文深入探讨Groovy的闭包概念,包括闭包的定义、格式、执行方式,以及代理策略中的this、owner和delegate的含义和区别。通过示例解释了不同代理策略对闭包行为的影响,强调了闭包在Groovy中的重要性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闭包概述

闭包就是一个特殊的匿名代码块,可以传递参数,有返回值,还能作为方法的参数进行传递。

闭包格式
  1. 闭包的格式定义如下:

    { [closureParameters -> ] statements }

    示例:

    { item++ }                                          
    
    { -> item++ }                                       
    
    { println it }                                      
    
    { it -> println it }                                
    
    { name -> println name }                            
    
    { String x, int y ->                                
        println "hey ${x} the value is ${y}"
    }
    
    { reader ->                                         
        def line = reader.readLine()
        line.trim()
    }
  2. closureParameters :闭包参数是非必需的,与方法的参数十分类似,区别是:如果存在闭包参数,闭包参数与闭包语句之间需要使用箭头(->)的分割。

  3. 注意:如果没有指定参数,则默认存在一个it的参数,代表的是闭包本身。如下例子:

    def obj = {
        println(it)
    }
    obj('hello')
  4. statements:同样,闭包语句则可以类比于方法体,功能也相同。

  5. 变量可作为闭包的载体,原因是:闭包其实是Groovy中的Closure类的实例。如下三种示例:

    def listener = { e -> println "Clicked on $e.source" }      
    
    //指明为Closure类型实例
    Closure callback = { println 'Done!' }                      
    
    //指明为Closure类型实例,并指定返回类型 
    Closure<Boolean> isTextFile = {
        File it -> it.name.endsWith('.txt')                     
    }
  6. 执行闭包有两种方式:一是直接调用;二是通过调用Closure的call方法。如下代码:

    def obj = {
        def item = 10
        return ++item
    }
    // 直接调用
    println(obj())
    
    //call调用
    println(obj.call())
  7. 从字节码来看上述两种方式的区别:两种方式完全等效,都是通过call来完成

    public Object test() {
        CallSite[] var1 = $getCallSiteArray();
        class _test_closure1 extends Closure implements GeneratedClosure {
            public _test_closure1(Object _thisObject) {
                CallSite[] var3 = $getCallSiteArray();
                super(Closures.this, _thisObject);
            }
    
            public Object doCall(Object it) {
                CallSite[] var2 = $getCallSiteArray();
                Object item = Integer.valueOf(10);
                return var2[0].call(item);
            }
    
            public Object doCall() {
                CallSite[] var1 = $getCallSiteArray();
                return this.doCall((Object)null);
            }
        }
    
        Object obj = new _test_closure1(this);
        var1[2].callCurrent(this, var1[3].call(obj));
        return var1[4].callCurrent(this, var1[5].call(obj));
    }
代理策略
  1. 代理在groovy的闭包中是非常重要的概念,与之相关的是this、owner和delegate这三种概念。

  2. this:指的是定义闭包所在的直接的外围类,具体有如下两种方式获取:

    class Test {
        void run() {
            // 方式一:使用getThisObject
            def enclosingThisObject = { getThisObject() }
            // 方式二:this操作符
            def enclosingThis = { this }
    
            def testThis = this
            def print = "enclosingThisObject=${enclosingThisObject()},enclosingThis=${enclosingThis()},testThis=$testThis"
            println(print)
       }
    }
    def test = new Test()
    test.run()

    运行结果:

    enclosingThisObject=com.demo.test.cl.Test@4f209819,
    enclosingThis=com.demo.test.cl.Test@4f209819,
    testThis=com.demo.test.cl.Test@4f209819

    从字节码也可看出两种方式是等效的,因为this方式也会调用getThisObject,如下:

    public void run() {
        CallSite[] var1 = $getCallSiteArray();
        class _run_closure1 extends Closure implements GeneratedClosure {
            public _run_closure1(Object _thisObject) {
                CallSite[] var3 = $getCallSiteArray();
                super(Test.this, _thisObject);
            }
    
            public Object doCall(Object it) {
                CallSite[] var2 = $getCallSiteArray();
                return var2[0].callCurrent(this);
            }
    
            public Object doCall() {
                CallSite[] var1 = $getCallSiteArray();
                return this.doCall((Object)null);
            }
        }
    
        Object enclosingThisObject = new _run_closure1(this);
        class _run_closure2 extends Closure implements GeneratedClosure {
            public _run_closure2(Object _thisObject) {
                CallSite[] var3 = $getCallSiteArray();
                super(Test.this, _thisObject);
            }
    
            public Object doCall(Object it) {
                CallSite[] var2 = $getCallSiteArray();
                // this方式调用getThisObject方法
                return this.getThisObject();
            }
    
            public Object doCall() {
                CallSite[] var1 = $getCallSiteArray();
                return this.doCall((Object)null);
            }
        }
    
        Object enclosingThis = new _run_closure2(this);
        Object print = new GStringImpl(new Object[]{var1[0].call(enclosingThisObject), var1[1].call(enclosingThis), this}, new String[]{"enclosingThisObject=", ",enclosingThis=", ",testThis=", ""});
        var1[2].callCurrent(this, print);
    }

    注意:在内部类中定义闭包,那么this指的是这个内部类;同样,嵌套的闭包定义,this指的是外层闭包所在的类。如下实例:

    class EnclosedInInnerClass {
        class Inner {
            Closure cl = { this }
        }
    
        void run() {
            def inner = new Inner()
            assert inner.cl() == inner
        }
    }
    
    class NestedClosures {
        void run() {
            def nestedClosures = {
                def cl = { this }
                cl()
            }
            assert nestedClosures() == this
        }
    }

    小结:闭包需要依附于某个类中,不能单独存在;this其实是闭包与所在外部类的通信桥梁。

  3. owner: 指的是定义闭包的直接外围对象,可以是类或者闭包。与this相似,区别在于嵌套闭包,如下实例:

    class NestedClosures {
        void runOwner() {
            def nestedClosures = {
                def cl = { owner }
                cl()
            }
            assert nestedClosures() == nestedClosures
        }
    
        void runThis() {
            def nestedClosures = {
                def cl = { this }
                cl()
            }
            assert nestedClosures() == this
        }
    }

    嵌套闭包中this与owner的区别:this是外围类的实例,而owner则是外层闭包实例。

    小结:闭包中的this侧重于直接外围类,而owner则偏向于闭包的宿主对象,它们都是闭包与外部环境的通信桥梁。

  4. delegate:获取闭包的delegate有如下两种方式,其返回的默认值是owner。

    // 方式一:调用getDelegate()方法
    def obj1 = { getDelegate() }
    // 方式二:delegate关键字
    def obj2 = { delegate }
    // delegate默认值是owner
    println(obj1() == obj2.owner)
    // 嵌套闭包delegate默认值也是owner
    def enclosed = {
          { -> delegate }.call()
        }
    assert enclosed() == enclosed

    如下通过不同的代理对象,闭包的行为是不同的:

    class Person {
        String name
    }
    
    class Thing {
        String name
    }
    
    def p = new Person(name: 'Norman')
    def t = new Thing(name: 'Teapot')
    
    def upperCasedName = { delegate.name.toUpperCase() }
    upperCasedName.delegate = p
    println(upperCasedName())//Norman
    
    upperCasedName.delegate = t
    println(upperCasedName())//Teapot
  5. 代理策略:上述例子中的闭包能正常运行,是因为闭包的代理策略影响了编译闭包代码时的解析策略。分为如下几种策略:

    • Closure.OWNER_FIRST:OWNER优先策略,也是默认策略,优先从owner中寻找属性或方法,找不到再从delegete中寻找。
    • Closure.DELEGATE_FIRST:与OWNER_FIRST相反。
    • Closure.OWNER_ONLY: 只在owner中寻找属性或方法。
    • Closure.DELEGATE_ONLY: 只在delegate中寻找属性或方法。
    • Closure.TO_SELF: 前提是用户需要实现的Closure的子类,只会在闭包自身中寻找属性或方法。
    class Person {
        String name
        int age
        def fetchAge = { age }
    }
    
    class Thing {
        String name
    }
    
    def p = new Person(name: 'Jessica', age: 42)
    def t = new Thing(name: 'Printer')
    def cl = p.fetchAge
    // 注意:cl的默认代理对象就是p,可以省略
    // cl.delegate = p
    println(cl())//42
    
    cl.delegate = t
    println(cl())//42
    
    //修改代理策略
    cl.resolveStrategy = Closure.DELEGATE_ONLY
    cl.delegate = p
    println(cl())//42
    
    cl.delegate = t
    try {
        cl()
        assert false
    } catch (MissingPropertyException ex) {
        // "age" is not defined on the delegate
    }
<think>好的,我现在需要帮助用户理解Groovy闭包语法和用法,并提供示例。首先,我需要回顾自己关于Groovy闭包的知识,确保准确性。根据用户提供的引用,闭包Groovy中是一个重要的特性,用于构建DSL、函数式编程和简化集合操作。 首先,用户需要语法和示例。根据引用[4],闭包的基本定义是使用大括号{},可以像函数一样调用,或者使用call方法。例如,def closure = { println "hello world!" },然后调用closure()或closure.call()。 接下来,带参数的闭包。引用[4]提到,闭包可以接受参数,比如{ param -> ... },如果只有一个参数,隐式参数it可用。例如,闭包{ println it },在each方法中使用,如friends.each { println "Hello, $it" }(引用[3])。 然后,闭包的委托机制。引用[1]详细说明了闭包的delegate属性,默认情况下delegate是owner,即定义闭包的外部类或闭包。用户可能需要示例来说明如何改变委托,比如在构建DSL时,将委托设置为其他对象,以访问该对象的方法和属性。 此外,闭包作为方法参数。引用[3]中的each方法接受闭包作为参数,展示了如何将闭包传递给集合方法。需要示例说明如何定义接受闭包作为参数的方法,以及闭包在其中的使用。 还需要注意闭包的返回值。如果闭包的最后一行是表达式,它将作为返回值。例如,闭包{ a + b }会返回a+b的结果。 现在,综合这些信息,我需要组织一个结构清晰的回答,包括基本语法、参数传递、委托机制、作为方法参数的示例,以及闭包的返回值。同时,确保所有代码示例正确使用闭包语法,比如参数使用箭头->,隐式参数it的情况,以及委托的设置。 可能还需要提到闭包的作用域和变量捕获,比如闭包可以访问外部变量,但需要说明闭包如何绑定这些变量。不过根据用户提供的引用,可能暂时不需要深入这点,除非用户的问题中有相关部分。当前用户的问题集中在语法和示例,所以应优先展示这些内容。 最后,根据用户提供的引用[2],闭包Groovy中的重要性,可以强调其灵活性和在集合操作中的应用,如each、collect等方法的使用。 检查是否有遗漏的部分,比如闭包的隐式参数、委托的设置方法(delegate属性或getDelegate方法),以及如何将闭包传递给方法。确保每个示例都能正确运行,并符合用户提供的引用中的代码风格。 现在,将这些内容组织成步骤分明的部分,每个部分包含示例代码,并适当引用用户提供的参考资料,比如在讨论委托时引用[^1],在集合操作时引用[^3]和[^4]。</think>### Groovy闭包语法与用法详解 #### 1. **基本语法** Groovy闭包是用大括号`{}`定义的代码块,可以像函数一样被调用。 ```groovy // 定义无参数闭包 def closure = { println "Hello, Groovy!" } closure() // 输出: Hello, Groovy! closure.call() // 等效调用 ``` #### 2. **带参数的闭包** - **显式参数**:通过`->`声明参数。 ```groovy def add = { a, b -> a + b } println add(3, 5) // 输出: 8 ``` - **隐式参数**:若闭包只有一个参数,可通过`it`访问。 ```groovy def printItem = { println it } ["Apple", "Banana"].each(printItem) // 输出: Apple\nBanana ``` #### 3. **闭包作为方法参数** Groovy集合方法(如`each`、`collect`)常接收闭包处理元素[^3][^4]。 ```groovy // 直接传递闭包到each方法 (1..3).each { println "Number: $it" } // 输出: // Number: 1 // Number: 2 // Number: 3 ``` #### 4. **闭包的委托机制** 闭包可通过`delegate`属性访问外部作用域外的对象,常用于构建DSL[^1]。 ```groovy class Person { String name = "Alice" } def closure = { "Name: ${name}" } closure.delegate = new Person() // 设置委托对象 println closure() // 输出: Name: Alice ``` #### 5. **闭包返回值** 闭包默认返回最后一行表达式的值。 ```groovy def square = { num -> num * num } println square(4) // 输出: 16 ``` #### 6. **嵌套闭包与作用域** 嵌套闭包的`delegate`默认指向其直接所有者(Owner)[^1]。 ```groovy def outer = { def inner = { delegate } inner() } println outer() == outer // 输出: true(inner的delegate是outer) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值