天天看點

2Groovy閉包與方法引用Groovy閉包與方法引用this、owner和delegate方法引用操作符.&SAM(single abstract method)Thanks to

Groovy閉包與方法引用

本文主要介紹閉包,方法引用相關知識。

閉包是

{ [closureParameters -> ] statements }

這種形式的代碼塊,可以接收參數,傳回值,并且可以複制給某個變量,閉包裡面可以引用外部的變量。具體可參考http://docs.groovy-lang.org/next/html/documentation/#_closures ,裡面說的很詳細。

閉包肯定會傳回值,不像方法可能無傳回值。

this、owner和delegate

每個閉包都有這3個概念,this、owner和delegate,這3個概念很像,容易搞糊塗。

  • this:定義閉包的class對象
  • owner:定義閉包的class對象或者閉包對象
  • delegate:一個第三方對象,如果閉包内找不到方法或者屬性就去delegate裡面找。

this

一個閉包的this指向的必然是一個class對象,而不是閉包對象,如下所示,

  • Enclosing内的閉包whatIsThis的this指向Enclosing對象。
  • 而EnclosedInInnerClass裡面,閉包cl定義在Inner裡面,是以cl的this指針是Inner對象而不是EnclosedInInnerClass對象。
  • NestedClosures裡面的閉包cl是定義在另一個閉包nestedClosures裡面的,是以cl的this是NestedClosures對象,而不是nestedClosures閉包
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                     
    }
}
           

owner

owner跟this基本一樣,唯一不同就是owner可以是閉包。是以下邊代碼裡的cl的owner就是nestedClosures。

class NestedClosures {
    void run() {
        def nestedClosures = {
            def cl = { owner }                               
            cl()
        }
        assert nestedClosures() == nestedClosures            
    }
}
           

delegate

delegate是groovy裡比較難了解的概念,簡單的說如果一個閉包在執行的時候發現某個參數未定義,那麼就會去他的owner以及delegate裡面找(預設是先owner後delegate)。其實這麼說起來delegate就是相當于java裡的非靜态内部類持有外部類的引用,delegate就是外部類的引用。預設情況下delegate就是owner。delegate在DSL裡面是非常有用的。

例子1

如下所示,say這個閉包需要m變量,但是m未定義,如果直接執行say()肯定不行,在這裡我們給say.delegate=eee,而eee這個map裡邊是有m這個變量的,是以在L9執行的時候,發現閉包内m未定義,就去他的delegate eee裡找,找到了m,是以最後結果就是列印出2

def say = {
    println m
}
//say.delegate = [m:]

eee=[m:]
assert eee.m==
say.delegate=eee
say()
           

例子2

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
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'
           

在這裡,我們定義了一個閉包upperCasedName,通過指定delegate,改變閉包的執行主體,這看起來和傳函數參數類似。閉包的delegate是可以不寫的,因為閉包找不到成員的時候就會調用delegate的方法,是以閉包可以這麼寫

字元串插值

我們知道在字元串插值的時候,可以用

${}

占位,裡面放任意表達式。實際上還可以用

${->}

占位裡面放閉包表達式。此時有一個特性,惰性求值(lazy evaluation).如下所示,對于普通的插值表達式eagerGString,被指派為1之後,将不再改變。而對于閉包插值lazyGString來說,他每次GString轉換為String的時候都會調用閉包,生成一個新的字元串

def number =  
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 1" 

number =  
assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 2" 
           

其他

如果一個函數與一個閉包擁有相同的名稱和參數,則調用的時候将執行函數,而不是閉包

方法引用操作符

.&

groovy支援方法引用,如下所示,upper就是一個方法引用,方法引用操作符的左邊是個對象(還可以是this),右邊是方法名。

對象.&方法名

注意下邊fun是一個閉包,fun()是字元串‘EXAMPLE OF METHOD REFERENCE’

def str = 'example of method reference'
def str2="s"
def fun = str.&toUpperCase
def upper = fun()

assert upper == str.toUpperCase()
assert upper != str2.toUpperCase()
println(upper)
println(fun)
println(fun())
assert fun instanceof Closure
assert fun() instanceof String
           

this.&方法名-方法無參數

方法無參數的時候,閉包就相當于一個簡單的函數指針。

def f(){
 println "Hello,world!";
}

def s=this.&f;

s();
           

this.&方法名-方法有參數

這個就更吊了,隻要有方法名,可以通路不同類型的對象,如下所示,reference是個閉包,往裡面傳個’foo’就會調用doSomething(String str)方法,往裡頭傳個123,就會調用doSomething(Integer x)方法,這個簡直是多态的更新版。

def doSomething(String str) { str.toUpperCase() }                                   
def doSomething(Integer x) { *x }                                                  
def reference = this.&doSomething                                                   
assert reference('foo') == 'FOO'                                                    
assert reference()   ==  
           

單參數閉包隐藏參數

如下所示filter是個閉包,他有且隻有一個參數,這個參數就可以隐藏,用到的時候用it來指代(用法有點像this)

def filter = { it.contains 'G' }
assert filter("Groovy")==true
           

SAM(single abstract method)

A SAM type is a type which defines a single abstract method. This includes:

interface Predicate<T> {
    boolean accept(T obj)
}
           

Thanks to

http://docs.groovy-lang.org/next/html/documentation/#_closures

groovy 官方sdk

http://groovy.zeroleaf.com/

http://wiki.jikexueyuan.com/project/deep-android-gradle/four-two.html

http://dublintech.blogspot.hk/2014/05/groovy-closures-this-owner-delegate.html

繼續閱讀