Groovy閉包:this、owner、delegate(規(guī)格文件節(jié)選翻譯)

這一段翻譯自Groovy的規(guī)格文件的 3.2 Owner, delegate and this

3.2. Owner, delegate and this

為了明白delegate的概念,我們必須首先解釋清楚在閉包中關(guān)鍵字this的意義。一個(gè)閉包通常定義了一下三個(gè)量:

  • this 指的是閉包定義所在的類
  • owner 指的是閉包定義所在的對象,這個(gè)對象可能是類也可能是另一個(gè)閉包。【這是因?yàn)殚]包是可以嵌套定義的。】
  • delegate 指的是一個(gè)第三方的(委托)對象,當(dāng)在閉包中調(diào)用的方法或?qū)傩詻]有定義時(shí),就會(huì)去委托對象上尋找。

3.2.1. this的意義

在閉包中,調(diào)用getThisObject返回這個(gè)閉包定義所在的對象。它等價(jià)于使用一個(gè)顯式的this:

class Enclosing {
    void run() {
        def whatIsThisObject = { getThisObject() }  //#1 
        assert whatIsThisObject() == this           //#2
        def whatIsThis = { this }                           //#3
        assert whatIsThis() == this                        //#4 
    }
}
class EnclosedInInnerClass {
    class Inner {
        Closure cl = { this }                               //#5
    }
    void run() {
        def inner = new Inner()
        assert inner.cl() == inner                        //#6  
    }
}
class NestedClosures {
    void run() {
        def nestedClosures = {
            def cl = { this }                               //#7
            cl()
        }
        assert nestedClosures() == this                //#8     
    }
}
  1. Enclosing中定義一個(gè)閉包,并返回getThisObject
  2. 調(diào)用閉包將返回Enclosing類的一個(gè)實(shí)例。
  3. 通常你會(huì)使用這種便捷的語法
  4. 并且它將會(huì)返回同一個(gè)對象
  5. 如果閉包定義在一個(gè)內(nèi)部類中
  6. 則閉包中的this將返回這個(gè)內(nèi)部類,而不是頂級類
  7. 在閉包嵌套的情況中,如此處的c1閉包定義在nestedClosures閉包中
  8. 那么this指的就是離c1最近的外部類,而不是外閉包?。?/strong>,

當(dāng)然有可能以以下的方式從包裹類中調(diào)用方法:

class Person {
    String name
    int age
    String toString() { "$name is $age years old" }

    String dump() {
        def cl = {
            String msg = this.toString()               //#1
            println msg
            msg
        }
        cl()
    }
}
def p = new Person(name:'Janice', age:74)
assert p.dump() == 'Janice is 74 years old'

該閉包在this上調(diào)用了toString方法,即外層包裹類的toString方法,它返回了Person類實(shí)例的一個(gè)描述。


3.2.2. 閉包的Owner

閉包的owner屬性和閉包的this非常相似,但是有一點(diǎn)微妙的差別:它返回這個(gè)閉包的直接包裹對象,可能是外層的嵌套閉包,也可能是一個(gè)類。

class Enclosing {
    void run() {
        def whatIsOwnerMethod = { getOwner() }    //#1           
        assert whatIsOwnerMethod() == this            //#2       
        def whatIsOwner = { owner }                     //#3     
        assert whatIsOwner() == this                  ///#4       
    }
}
class EnclosedInInnerClass {
    class Inner {
        Closure cl = { owner }                         //#5      
    }
    void run() {
        def inner = new Inner()
        assert inner.cl() == inner                       //#6    
    }
}
class NestedClosures {
    void run() {
        def nestedClosures = {
            def cl = { owner }                               //#7
            cl()
        }
        assert nestedClosures() == nestedClosures            //#8
    }
}
  1. 定義在Enclosing類中的閉包,返回getOwner
  2. 調(diào)用該閉包將返回Enclosing的實(shí)例
  3. 通常會(huì)使用這種簡潔的語法
  4. 返回同一個(gè)對象
  5. 如果閉包是在一個(gè)內(nèi)部類中定義的
  6. owner指定是內(nèi)部類,而不是頂級類
  7. 但是在嵌套閉包的情況下,如此處的c1閉包定義在nestedClosures閉包內(nèi)
  8. owner指的是外層嵌套的閉包,這一點(diǎn)與this不同??!

3.2.3. 閉包的delegate

閉包中的delegate可以通過delegate屬性或者調(diào)用getDelegate方法來訪問。它是Groovy中幫助建立領(lǐng)域特定語言(domain specific languages)的強(qiáng)大的工具。閉包中的thisowner指的是閉包中的詞法作用域,而delegate則是一個(gè)可由用戶定義的對象。默認(rèn)情況下,delegate等于owner:

class Enclosing {
    void run() {
        def cl = { getDelegate() }           //#1               
        def cl2 = { delegate }                       //#2       
        assert cl() == cl2()                                //#3
        assert cl() == this                             //#4    
        def enclosed = {
            { -> delegate }.call()                          //#5
        }
        assert enclosed() == enclosed                   //#6    
    }
}
  1. 通過調(diào)用getDelegate()得到閉包的delegate對象
  2. 或使用delegate屬性
  3. 二者都返回同一個(gè)對象
  4. 都是外圍的包裹類或閉包
  5. 尤其是在嵌套閉包的情況
  6. 此時(shí)delegate就是owner

閉包的delegate屬性可以被修改成任何對象。讓我們通過創(chuàng)建兩個(gè)類來說明這一點(diǎn),這兩個(gè)類都不是互相的子類但是它們都定義了一個(gè)名為name的屬性:

class Person {
    String name
}
class Thing {
    String name
}

def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')

然后我們定義一個(gè)閉包,它從delegate上取name屬性并返回:

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

通過改變delegate,我們可以看到目標(biāo)對象將被改變:

upperCasedName.delegate = p
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'

此時(shí)。這種效果和使用一個(gè)定義在閉包的詞法作用域內(nèi)的target對象沒有差別:

def target = p
def upperCasedNameUsingVar = { target.name.toUpperCase() }
assert upperCasedNameUsingVar() == 'NORMAN'

但是本質(zhì)上卻有重要區(qū)別:

  • target是一個(gè)局部變量的引用
  • delegate使用時(shí)可以不帶有前綴

3.2.4. 委托策略

閉包內(nèi)任何時(shí)候,只要使用一個(gè)沒有被引用的屬性,就會(huì)委托給delegate對象

class Person {
    String name
}
def p = new Person(name:'Igor')
def cl = { name.toUpperCase() }//#1                 
cl.delegate = p            //#2                                 
assert cl() == 'IGOR'    //#3  
  1. name沒有被閉包所在的詞法作用域內(nèi)任何一個(gè)變量引用
  2. 改變delegate,使之指向一個(gè)Person類實(shí)例
  3. 方法調(diào)用成功

這段代碼能夠工作的原因是因?yàn)?code>name屬性將會(huì)被委托給delegate對象執(zhí)行!這是在閉包內(nèi)解析屬性或方法調(diào)用的一種很有效的方法,從而沒有必要設(shè)置一個(gè)顯式的delegate.receiver:根據(jù)默認(rèn)的閉包委托策略,這種行為是可以的。一個(gè)閉包定義了多種可供選擇的解析策略:

  • Closure.OWNER_FIRST是默認(rèn)策略。如果一個(gè)屬性/方法存在于owner上,那么就會(huì)在owner上調(diào)用;否則,就會(huì)委托給delegate
  • Closure.DELEGATE_FIRST和上面策略剛好相反: delegate首先被委托,其次再委托給owner
  • Closure.OWNER_ONLY即只在owner上解析屬性/方法, delegate被忽略。
  • Closure.DELEGATE_ONLY即只在delegate上解析屬性/方法, owner被忽略。
  • Closure.TO_SELF 可以被那些需要高級的元編程技術(shù)并卻希望實(shí)現(xiàn)自定義的解析策略的開發(fā)者使用:解析并不是在delegate或者owner上進(jìn)行,而是只在閉包類本身上進(jìn)行。只有實(shí)現(xiàn)了自己的Closure子類,這么使用才是被允許的。

讓我們用代碼解釋這個(gè)owner first的默認(rèn)策略:

class Person {
    String name
    def pretty = { "My name is $name" }        //#1     
    String toString() {
        pretty()
    }
}
class Thing {
    String name                              //#2       
}

def p = new Person(name: 'Sarah')
def t = new Thing(name: 'Teapot')

assert p.toString() == 'My name is Sarah'           //#3
p.pretty.delegate = t                               //#4
assert p.toString() == 'My name is Sarah'   //#5
  1. 定義一個(gè)閉包成員變量,它引用了name屬性
  2. Thing類和Person類都定義了name屬性
  3. 使用默認(rèn)策略,name屬性會(huì)在owner上解析(斷言為真)
  4. 改變delegate使之指向Thing的實(shí)例
  5. 并沒有什么被改變,依然在owner上解析(斷言為真)

然而我們可以改變這種默認(rèn)策略:

p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'

通過改變resolveStrategy,我們可以修改Groovy解析“隱式this”引用的方式:在這種情況下,name會(huì)首先在delegate上查詢,如果沒找到,則到owner上。因?yàn)?code>name在delegate(引用Thing類的實(shí)例)上有定義,所以被使用。
"delegate first"和 "delegate only"(或"owner first"和"owner only") 之間的不同是,如果delegate(或owner)上并沒有這種方法或者屬性,例如:

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.delegate = p
assert cl() == 42
cl.delegate = t
assert cl() == 42
cl.resolveStrategy = Closure.DELEGATE_ONLY
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
    cl()
    assert false
} catch (MissingPropertyException ex) {
    // "age" is not defined on the delegate
}

在這個(gè)例子中,我們定義了兩個(gè)類,它們都含有name屬性但是只有Person類聲明了age屬性。Person類同時(shí)也聲明了一個(gè)閉包,其中引用了age屬性。我們可以將默認(rèn)解析策略從"owner first"改變到"delegate only"。因?yàn)檫@個(gè)閉包的ownerPerson類,所以我們可以檢查delegate是不是Person類的一個(gè)實(shí)例,如果是,那么就能成功調(diào)用這個(gè)閉包,但是我們將它的delegate改成了Thing類的實(shí)例,所以運(yùn)行時(shí)會(huì)報(bào)groovy.lang.MissingPropertyException異常。盡管這個(gè)閉包被定義在Person類中,但是owner并沒有被使用。

關(guān)于如何利用這一特性來開發(fā)DSL(領(lǐng)域特定語言)的詳細(xì)說明可以參照:dedicated section of the manual.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 努力的人,應(yīng)該像好色那樣好學(xué) 做Android開發(fā)的同學(xué),對Gradle肯定不陌生,我們用它配置、構(gòu)建工程,可能還...
    HitenDev閱讀 13,660評論 9 50
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,602評論 30 472
  • 本文介紹了Groovy閉包的有關(guān)內(nèi)容。閉包可以說是Groovy中最重要的功能了。如果沒有閉包,那么Groovy除了...
    樂百川閱讀 7,733評論 3 13
  • 上上周日經(jīng)歷了徹底的絕望,痛徹心扉。難過到上周一的晚上,突然靈光乍現(xiàn),仿佛一下看到了“真相”,然后所有的情緒瞬間平...
    就讓自己在這里閱讀 307評論 0 0

友情鏈接更多精彩內(nèi)容