1. 程式人生 > >Groovy語法之閉包

Groovy語法之閉包

閉包概述

閉包就是一個特殊的匿名程式碼塊,可以傳遞引數,有返回值,還能作為方法的引數進行傳遞。

閉包格式

  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
    }

相關推薦

Groovy語法

閉包概述 閉包就是一個特殊的匿名程式碼塊,可以傳遞引數,有返回值,還能作為方法的引數進行傳遞。 閉包格式 閉包的格式定義如下: { [closureParameters -> ] s

Groovy學習——

一、簡介:Groovy是動態弱型別語言,即Groovy不要求宣告變數的型別、方法的引數或者方法的返回值。這意味著在不同環境下,變數可以以多種方式使用。閉包是一個型別為groovy.lang.Closure的程式碼塊,與其他語言的lambdas特性類似。閉包可以賦值給變數,作為

Groovy語法

什麼是閉包? 使用{}包起來的程式碼塊就是閉包 println {}輸出結果為: xx.com.xx.ClosureDemorunclosure1@102d1符合格式:類匿名內部類@hash code 閉包就是一個匿名內部類的物件。 也可以建立一個有名

go語言基礎基匿名函式本語法

一、匿名函式 示例1: package main import "fmt" func main() { a := 10 str := "mike" //匿名函式,沒有函式名字, 函式定義,還沒有呼叫 f1 := func() { //:= 自動推導型別 fmt

Groovy語言規範中文版

閉包官方文件:Closures 這一章節介紹Groovy閉包。Groovy中的閉包是開放,匿名,且可以帶引數的程式碼塊,返回一個值並可被賦值給變數。一個閉包可以是被大括號包圍的幾個變數的宣告。不太正式的閉包,在Groovy語言中可以包含無限多個定義在大括號外的

看懂Gradle指令碼(2)- Groovy語言的語法

本篇文章討論下面這一小段Gradle指令碼: repositories { mavenCentral() } 閉包字面量 閉包字面量看起來像Java裡的程式碼塊:用一對兒花括號包起來。前面

JavaScript(重新認識)

log 變量 for 局部變量 ava logs 所在 數組函數 使用 最近又重新學習了閉包,發現之前沒有深刻理解作用域鏈,學習作用域鏈後對閉包才可以做到真正的理解。 閉包是指有權另一個函數作用域中變量的函數。要理解閉包首先理解作用域鏈。

python函數 裝飾器 作業

word bold 多個 格式 color ebe 優先 時間 函數返回 一:編寫函數,(函數執行的時間是隨機的) import randomdef t(): time.sleep(random.randrange(1,3)) print(‘hello‘)二:編

Day 19 函數、裝飾器

false print glob src true success 返回值 count please 一、什麽是裝飾器 器即函數 裝飾即修飾,意指為其他函數添加新功能 裝飾器定義:本質就是函數,功能是為其他函數添加新功能 二、裝飾器遵循的原則 1.不修改被裝飾函數

Swift學習筆記

pps eap animate nbsp ssi apps arr 全局 mef 簡介 (真的很簡) 閉包的完整形態是這個樣子的: { (parameters) -> returnType in statements } 寫在一行裏就是

JavaScript

線程 rom not 第一個 包含 osi 新的 其中 mozilla 第一部分:基本概念   我們知道根據作用域鏈的規則,一個函數是不能訪問到在與他同一個作用域內的函數內的內部變量的,如下所示: function foo() {

每天學一點Scala

閉包 匿名函數 scala 定義函數在變量不處於其有效作用域時,還能夠對變量進行訪問,即為閉包;也就是說,變量超出了其作用域,還可以使用,就是閉包現象。可能針對的使用場景之一?比方說,年底了,公司針對每個部門的獎金是不同。比方說,銷售部門獎金為10000,研發部門是15000,測試部門12000等

lua學習實現原理

引入 內嵌 種類 同時 概念比較 就會 類型 種類型 賦值語句 感覺學習lua的過程中, 閉包的概念比較難以理解,這裏記錄下對閉包的學習。 閉包的概念 在Lua中,閉包(closure)是由一個函數和該函數會訪問到的非局部變量(或者是upvalue)組成的,其中

python

() 一個 inner pre pri ret def outer pos 閉包是一個python的現象,我們在學習裝飾器的時候會用到閉包def outer(): x=10 def inner():#條件一,inner就是內部函數 print(x)

javascript,遞歸,深拷貝

好處 宋體 get nat style javascrip div span ces 閉包 理解:a函數執行後return出b函數且b函數可以訪問a函數的數據 好處:子函數存儲在復函數內部,子函數執行完不會被自動銷毀 壞處:占用內存比較大 ex: function bib

面試 return

urn 調試 time 間隔 fun argument repeat returns AI https://blog.csdn.net/FE_dev/article/details/74124373 經典面試題解釋 return 函數 ,在()之後才會執行 請定義這樣

Python裝飾器

*args func tro 普通 args 示例 return 事情 通用 1.什麽是閉包?多層函數嵌套,(函數裏面還有定義函數,一般是兩個),往往內層函數會用到外層函數的變量,把內層函數以及外部函數的變量當成一個特殊的對象,這就是閉包。閉包比面向對象更純凈、更輕量,既有

Python函數式編程

問題 cto lob num ret ber 全局變量 bre mil -------------------------函數式編程之*******閉包------------------------ Note: 一:簡介 函數式編程不是程序必須要的,但是對於簡化程序

JavaScript基礎概念----

scrip javascrip bsp style ole fun 閉包 rip 能夠 閉包 是由作用域鏈引起的。 var parent = function(){ var a = ‘hello‘; return function(){ c

Python3

何為閉包 維基百科中關於閉包的概念: 在一些語言中,在函式中可以(巢狀)定義另一個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。閉包可以用來在一個函式與一組“私有”變數之間建立關聯關係。在給定函式被多次呼叫的過程中,這些私有變數能夠保持其永續性。   閉包條件