1. 程式人生 > >gradle學習筆記(三) Groovy閉包

gradle學習筆記(三) Groovy閉包

前言:

1. 閉包基礎:

1. 概念:
A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable.
Groovy中的閉包是一段 開放的、匿名的程式碼。

2. 閉包格式:

     {
          [closureParameters -> ] 
          statements 
      }

閉包[closureParameters->] 是可選的,看起來像一個List,並且這些引數的型別可以不寫。

3. 舉個例子:

{ String x, int y -> //引數                                
    println "hey ${x} the value is ${y}" //statement語句
}

2. 閉包用法

第一步,建立閉包變數:

建立閉包有三種方法:

 //使用 def 宣告一個閉包
 def listener = { e -> println "Clicked on $e.source" }

 //使用指定型別 Closure 宣告一個閉包
 Closure callback = { println 'Done!'
} //使用泛型 宣告一個固定返回型別的閉包 Closure<Boolean> isTextFile = { File it -> it.name.endsWith('.txt') //返回boolean型別 }

第二步,呼叫閉包:

閉包是一個匿名的程式碼塊,不能像呼叫方法那麼呼叫。下面說明怎麼呼叫閉包。

示例1,無參閉包

def code = {123}; //省去了引數和箭頭
println code(); //呼叫該閉包
println code.call(); //另外一種呼叫方式

示例2,帶引數閉包


//是否為奇數
def isOddNumber = {
  int
i -> //宣告引數 i%2 == 1;//可以省略return語句 }; println isOddNumber(3); println isOddNumber.call(3);

3. 閉包引數說明:

第一種,普通的引數

  • 可以不寫引數型別
  • 可以給引數一個預設值
def closureWithOneArg = { str -> str.toUpperCase() }; //不寫引數型別
println closureWithOneArg('groovy') == 'GROOVY' //傳入錯誤型別會報錯

//另一個例子
def closureWithTwoArgs = { a,b -> a+b } 
println closureWithTwoArgs(1,2); //傳入不能相加的型別會報錯

//預設引數值
def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
println closureWithTwoArgAndDefaultValue(1); //呼叫時候都可以少寫一個,很方便

第二種,隱式引數 Implicit parameter

如果閉包沒定義引數的話,則隱含有一個引數,這個引數名字叫 it
it 代表閉包的引數。

def greeting = { "Hello, $it!" }; //隱含引數it

//相當於
def greetingT = {
    it ->
    // return "Hello , $it"; //GString 用法
    return "Hello, " + it + "!";
}
println greeting("Patrick");


//所以上面那個判斷奇數的閉包就可以這樣寫
def isOddNumber = { it % 2 == 1};
println isOddNumber(3);

因為在不指定引數時,有一個預設引數 it,那麼該怎麼指定無參的閉包呢。是這樣:

def magicNumber = { -> 42 }; //使用箭頭表示前面無參!!
magicNumber();

第三種,可變引數

//可變引數
def aMethod = {
  int... args ->  //  ...表示可變
  int sum = 0;
  args.eachWithIndex{ //遍歷求和
  it,i ->
  sum = sum + args[i];
  }
  return sum;
};
println aMethod(1,2,3);

4. 閉包的Delegation代理

The ability to change the delegate or change the delegation strategy of closures make it possible to design beautiful domain specific languages (DSLs) in Groovy.
Groovy中的閉包具有能夠改變代理或代理策略的這種能力,使得我們能夠使用它設計出很優美的DSL語言。

要想理解Groovy中的delegation,必須要理解以下幾個概念

1. 理解this,owner

Groovy中的閉包內建了3個不同的物件, 可以直接使用:

  • this 指閉包所在的最近的類 .class
  • owner 指定義閉包的宿主,不僅僅是類,還可能是一個閉包
  • delegate 代理
class NestedClosures {
    void run() {
        def nestedClosures = {//巢狀閉包
            def cl = { owner } //owner指的就是該閉包的宿主, 即nestedClosures,即第一層閉包                             
            cl()
        }
        assert nestedClosures() == nestedClosures            
    }
}


class NestedClosures {
    void run() {
        def nestedClosures = {//巢狀閉包
            def cl = { this }//指的是最近的class,即NestedClosures.class的示例物件                     
            cl()
        }
        assert nestedClosures() == this                     
    }
}

2. 理解delegate

While this and owner refer to the lexical scope of a closure, the delegate is a user defined object that a closure will use.
this和scope是Groovy內建在closure中的物件,那麼delegate就是使用者指定的一個物件,用於closure使用。delegate預設使用的是owner。

基礎介紹:

class Enclosing {
    void run() {
        def cl = { getDelegate() }//使用getDelegete()方法獲取closure的delegate
        def cl2 = { delegate } //  使用 delegate獲取
        assert cl() == cl2()    
        assert cl() == this  //預設使用owner,最近的類
        def enclosed = {
            { -> delegate }.call()                        
        }

        //預設使用owner,最近的閉包物件  
        assert enclosed() == enclosed;           
    }
}

delegate可以被設定為不同的物件

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

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

//定義一個閉包,使用delegate獲取名字,並返回大寫值
def upperCasedName = { delegate.name.toUpperCase() }

upperCasedName.delegate = p //設定為p,則返回NORMAN
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t //設定為t,則返回TEAPOT
assert upperCasedName() == 'TEAPOT'


/*
the delegate can be used transparently, that is to say without prefixing method calls with delegate.
delegate物件可以被透明的使用,即不需要在方法前面顯示宣告delegate.
*/
class Person {
    String name
}
def p = new Person(name:'Igor')

//並沒有delegate.name,預設是owner,owner沒有name屬性,所以使用delegate的name屬性
def cl = { name.toUpperCase() }              
cl.delegate = p //設定delegate                                 
assert cl() == 'IGOR'   

2. delegate strategy 代理的代理策略

上面提到的最後一個示例,就是使用了代理策略。delegate的策略預設是Closure.OWNER_FIRST ,所有在宿主沒有name屬性時,就會使用delegate的屬性,如果delegate也沒有,那就報錯啦。

delegate有如下幾個策略:

  • Closure.OWNER_FIRST is the default strategy. If a property/method exists on the owner, then it will be called on the owner. If not, then the delegate is used.

  • Closure.DELEGATE_FIRST reverses the logic: the delegate is used first, then the owner

  • Closure.OWNER_ONLY will only resolve the property/method lookup on the owner: the delegate will be ignored.

  • Closure.DELEGATE_ONLY will only resolve the property/method lookup on the delegate: the owner will be ignored.

  • Closure.TO_SELF can be used by developers who need advanced meta-programming techniques and wish to implement a custom resolution strategy: the resolution will not be made on the owner or the delegate but only on the closure class itself. It makes only sense to use this if you implement your own subclass of 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.delegate = p //設定代理
assert cl() == 42 
cl.delegate = t
assert cl() == 42 //owner優先,fetchAge是一個閉包,它的owner是Person

cl.resolveStrategy = Closure.DELEGATE_ONLY //修改策略
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
    cl()
    assert false //呵呵,delegate上面沒有該屬性,報錯
} catch (MissingPropertyException ex) {
    // "age" is not defined on the delegate //沒有定義
}

結語

這一篇文章主要學習了閉包的基本用法和基本概念,幾乎全是看著官方文件過來的,有點像翻譯了。目前對閉包的瞭解還是比較基礎,之後不斷在不斷的使用過程中再加深理解。閉包這塊很多語言都有該特性,像C、swift、js等等,在學習Groovy時候打好基礎,對之後的學習肯定有幫助。

恩,文章幾乎差不多翻譯了官方文件,若是有誤的地方,多多勘正啊。