微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

2Groovy闭包与方法引用

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:2]

eee=[m:2]
assert eee.m==2
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的方法,所以闭包可以这么写

def upperCasedname = { name.toupperCase() }

字符串插值

我们知道在字符串插值的时候,可以用${}占位,里面放任意表达式。实际上还可以用${->}占位里面放闭包表达式。此时有一个特性,惰性求值(lazy evaluation).如下所示,对于普通的插值表达式eagerGString,被赋值为1之后,将不再改变。而对于闭包插值lazyGString来说,他每次GString转换为String的时候都会调用闭包,生成一个新的字符串

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

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

number = 2 
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) { 2*x }                                                  
def reference = this.&doSomething                                                   
assert reference('foo') == 'FOO'                                                    
assert reference(123)   == 246

单参数闭包隐藏参数

如下所示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://www.voidcn.com/article/p-svqdmyjz-bnw.html
http://dublintech.blogspot.hk/2014/05/groovy-closures-this-owner-delegate.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐