Groovy中的閉包

Closures(閉包)

本節主要講groovy中的一個核心語法:closurs,也叫閉包。閉包在groovy中是一個處於代碼上下文中的開放的,匿名代碼塊。它能夠訪問到其外部的變量或方法。html

1. 句法

1.1 定義一個閉包

{ [closureParameters -> ] statements }

其中[]內是可選的閉包參數,可省略。當閉包帶有參數,就須要->來將參數和閉包體相分離。java

下面看一些閉包的具體例子:閉包

{ 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()
}

1.2 閉包也是對象

閉包在groovy中是groovy.lang.Closure類的實例,這使得閉包能夠賦值給變量或字段。學習

def listener = { e -> println "Clicked on $e.source" }      
assert listener instanceof Closure
Closure callback = { println 'Done!' }                      
Closure<Boolean> isTextFile = {
    File it -> it.name.endsWith('.txt')                     
}

1.3 閉包的調用

閉包有兩種調用方式:fetch

def code = { 123 }

assert code() == 123

assert code.call() == 123

閉包名+()或者閉包名.call()來調用閉包。gradle

2. 參數

2.1 正常參數

閉包的參數類型和前面講的方法的參數類型同樣,這裏很少說。this

2.2 含蓄的參數

當閉包沒有顯式聲明參數時,其默認包含一個隱式的參數itcode

def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

2.3 參數列表

參數列表的用法與普通方法同樣,這裏很少贅述。orm

3. 委託策略

委託策略是groovy中閉包獨有的語法,這也使得閉包較java的lambda更爲高級。下面簡單介紹一下groovy中的委託策略。htm

3.1 Owner,delegate和this

在理解delegate以前,首先先要了解一下閉包中this和owner的含義,閉包中三者是這麼定義的:

  • this 表示定義閉包的外圍類。
  • owner 表示定義閉包的直接外圍對象,能夠是類或者閉包。
  • delegate 表示一個用於處理方法調用和屬性處理的第三方類。

3.1.1 This

閉包中,使用this關鍵字或者調用方法getThisObject()來得到其外圍類:

class Enclosing {
    void run() {
        def whatIsThisObject = { getThisObject() }          
        assert whatIsThisObject() == this                   
        def whatIsThis = { this }                           
        assert whatIsThis() == 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表示的具體是哪一個對象能夠從this往外找,遇到的第一類就是this表明的類。

3.1.2 Owner

owner與this相似,只不過owner表示的是直接外圍對象,能夠是類也能夠是閉包:

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

上述例子與this中的例子不一樣的就是NestedClosures,其中owner表示的是nestedClosures而不是NestedClosures。

3.1.3 Delegate

閉包中可使用delegate關鍵字或者getDelegate()方法來獲得delegate變量,它默認與owner一致,但能夠由用戶自定義其表明的對象。

class Enclosing {
    void run() {
        def cl = { getDelegate() }                          
        def cl2 = { delegate }                              
        assert cl() == cl2()                                
        assert cl() == this                                 
        def enclosed = {
            { -> delegate }.call()                          
        }
        assert enclosed() == enclosed                       
    }
}

閉包中的delegate可被指向任意對象,咱們看下面這個例子:

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

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

定義了兩個擁有相同屬性name的類Person和Thing。接着定義一個閉包,其做用是經過delegate來得到name屬性。

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

接着改變閉包的delegate的指向,咱們能夠看到閉包調用結果也不一樣:

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

3.1.4 Delegate策略

在閉包中,當一個屬性沒有指明其全部者的時候,delegate策略就會發揮做用了。

class Person {
    String name
}
def p = new Person(name:'Igor')
def cl = { name.toUpperCase() }   //❶              
cl.delegate = p                   //❷              
assert cl() == 'IGOR'             //❸

能夠看到❶處的name沒有指明其全部者。即這個name屬性壓根不知道是誰的。在❷處指明cl的delegate爲p,這時候在❸處調用成功。

以上代碼之因此能夠正常運行是由於name屬性會被delegate處理。這是一個十分強大的方式用於解決閉包內的屬性的訪問或方法的調用。在❶處沒有顯示的使用delegate.name是由於delegate策略已經在程序運行的時候幫助咱們這樣作了。下面咱們看看閉包擁有的不一樣的delegate策略:

  • Closure.OWNER_FIRST 這是默認的策略,優先從owner中尋找屬性或方法,找不到再從delegete中尋找。上面的例子就是由於在owner中沒有找到name,接着在delegate中找到了name屬性。
  • Closure.DELEGATE_FIRST 與OWNER_FIRST相反。
  • Closure.OWNER_ONLY 只在owner中尋找。
  • Closure.DELEGATE_ONLY 只在delegate中尋找。
  • Closure.TO_SELF 在閉包自身中尋找。

下面咱們看一下默認的Closure.OWNER_FIRST的用法:

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

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

assert p.toString() == 'My name is Sarah'           
p.pretty.delegate = t                       //❶                          
assert p.toString() == 'My name is Sarah'   //❷

儘管在❶處將delegate指向了t,但由於是owner first的緣故,仍是會優先使用Person的name屬性。

略作修改:

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

這時候就會訪問t的name屬性了。

下面再來看一個例子:

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
}

當使用了Closure.DELEGATE_ONLY後,若delegate中找不到age屬性,則會直接報錯。

4. GStrings中的閉包

先來看一下下面這段代碼:

def x = 1
def gs = "x = ${x}"
assert gs == 'x = 1'

OK,運行沒有問題,那若是加兩行代碼呢?

x = 2
assert gs == 'x = 2'

這裏就會報錯了,錯誤緣由有兩:

  • GString只是調用了字符串的toString方法來得到值。
  • ${x}這種寫法並非一個閉包,而是一個表達式等價於$x,當GString被建立的時候該表達式會被計算。

因此當給x賦值2的時候,gs已經被建立,表達式也已經被計算,結果是x = 1,因此gs得值就是固定的x = 1。

若是要在GString使用閉包也是能夠的,以下:

def x = 1
def gs = "x = ${-> x}"
assert gs == 'x = 1'

x = 2
assert gs == 'x = 2'

總結

到這裏groovy中閉包的基本用法結束了,更多閉包的用請參考

還記得咱們學習groovy的目的是什麼嗎?對了,就是gradle。並且在gradle中使用了大量閉包的概念,因此在學習gradle以前還請好好掌握閉包這一節內容。😀

相關文章
相關標籤/搜索