天天看点

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

继续阅读