Groovy 闭包

闭包

闭包是一段可执行的代码块,类似于方法也可以传递参数;可在闭包中访问属性值,也就是说可以修改闭包作用域中的所有变量值;

简单实例

// name为闭包参数可有多个, '->' 之后为闭包执行代码体
def clos = { name -> println("${name} for better life!") }   // name为闭包参数
clos.call('YY')    // 执行闭包(显示调用)
clos('CC')    // 省略call() (隐士调用)

单个隐藏参数 it

// 单个隐参数 it
def cols2 = {println("${it} for better life!")}    // 访问it隐藏属性
cols2('YY')

闭包作为函数方法参数传递

def my_clos = { param1 -> println("Welcome ${param1}") }
def method(clos) {
    def name = 'better'
    clos(name)   // 调用闭包
}
method(my_clos)  // 迪用闭包

//=============== 特殊的语法格式:以后DSL会经常看到 ===========================
//  如果闭包是方法的最后一个参数,可将闭包写入括号外,如下:
// 闭包在实参参数的外部, '()' 也可以省略
method() { param1 -> println("Welcome ${param1}") }
// 省略括号
method { param1 -> println("Welcome ${param1}") }

// 例子:求1到100的和
println("===== 使用闭包求 1 到 100 的和 ===========")
def result = 0
1.upto(100) { result = result + it }    // 最后一个形参为闭包,放到括号外
println(result)

集合与字符串

举例:each方法原型为:each(Closure closure),源码为:

 public static  Iterator each(Iterator self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
        while (self.hasNext()) {
            Object arg = self.next();
            closure.call(arg);
        }
        return self;
    }

例子

    @Test
    void testClosure3() {
        def list = [1, 2, 3, 4, 5]
        list.each {
            if (it % 2 == 0) {
                println(it)     // 2 4
            }
        }

        def map = [k1: 'better', k2: 'zhaoyu']
        map.each {
                // 这里的闭包有2个参数, 查看api
            k, v -> println "key:$k, value:$v"
        }
    }

常用的一些闭包方法:

  • find(): 找到就返回;
  • findAll(): 找所有满足闭包条件的;
  • any(): 只要一个满足闭包条件就返回true,否则false;
  • every():必须每个item满足闭包条件才返回true,否则false;

collect与inject

collect: 闭包变更,返回新集合;
inject:传入初始值,遍历集合,不断与后一个累计处理

collect的简单用法:
println([1, 2, 3, 4].collect { it * 2 }) // 变更 (2,4,6,8)

collect的高级用法:map 转换的例子

   @Test
    void test() {
        def doubles = { item -> item * 2 }
        def triples = { item -> item * 3 }
        def isEven = { item -> item % 2 == 0 }

        println("Doubles: ${map(doubles, [1, 2, 3, 4])}")
        println("Triples: ${map(triples, [1, 2, 3, 4])}")
        println("Evens: ${map(isEven, [1, 2, 3, 4])}")
    }

    /**
     * map 转换方法
     * @param clos 闭包
     * @param list 集合
     */
    def map(clos, list) {
        return list.collect(clos)
    }

inject的例子

// 累加
def clos = (0..100).inject(0) { prev, element -> prev + element }
println("Sum : ${clos}")

闭包的其他特性

闭包是一种对象,所以可以作为方法的参数,如下:

闭包作为方法的参数

@Test
 void test3() {
     def isEven = { item -> item % 2 == 0 }
     def isOdd = { item -> !isEven(item) }
     println(filter([1, 2, 3, 4], isEven))
 }

/**
 * 闭包作为方法的参数
 * @param list
     * @param clos
     */
def filter(list, clos) {
    return list.findAll(clos)
}

闭包作为另一个闭包的参数

@Test
void test4() {
    // 闭包1
    def isEven = { item -> item % 2 == 0 }
    // 闭包2,使用闭包1作为参数
    def takeWhile = { closure, list ->
        def result = []
        for (item in list) {
            if (closure(item)) {
                result << item
            }
        }
        return result
    }

    println(takeWhile(isEven, [1, 2, 3, 4]))
}

返回值为闭包

@Test
void test5() {
    def twice = multiply(2)
    println("twice(4): ${twice(4)}")

    // == 闭包返回闭包 ==
    def multiplication = { x -> return { y -> x * y } }
    def quadruple = multiplication(4)
    println("quadruple(3): ${quadruple(3)}")

}
// 方法返回闭包
def multiply(x) {
    return { y -> return x * y }
}

闭包的嵌套

// 闭包嵌套闭包
@Test
void test6() {
    // 选择排序闭包
    def selectionSoft = { list ->
        def swap = { sList, p, q ->
            def temp = sList[p]
            sList[p] = sList[q]
            sList[q] = temp
        }

        // 找最小的闭包
        def minimumPosition = { pList, from ->
            def mPos = from
            def nextFrom = 1 + from
            for (j in nextFrom..

闭包的更多信息

闭包与方法

// 闭包
def twice = { n -> return 2 * n }
// 方法
def twice(n) { return 2 * n }

默认参数,与方法类型

// 默认参数
def greetting = { msg, name = 'better' -> println("${msg} ---> ${name}") }
println(greetting("Hello"))

return 语句

闭包中return的语法比较重要,如:为确定某个item是否在某个列表之中,通常使用 find()方法;
如下:

def hasMemberA(item, list) {
    def size = list.size()
    for (index in 0..
        if (item == it) {
            return true
        }
    }
    return false
}

def numbers = [11,12,13,14]
println("hasMemberA(11, numbers) = ${hasMemberA(11, numbers)}")     // true
println("hasMemberB(11, numbers) = ${hasMemberB(11, numbers)}")     // 永远false

hasMemberB() 方法之所以返回false,我们看看each闭包的实现,迭代器被用于检遍历列表中对象,并调用闭包;示例中的实参的闭包语句,实际上是在闭包中执行的,跟方法没有关系,这样while循环一直执行,知道遍历完毕,所以hasMembersB()方法返回了false;

public static  Iterator each(Iterator self, @ClosureParams(FirstGenericType.class) Closure closure) {
        while(self.hasNext()) {
            Object arg = self.next();
            closure.call(arg);
        }
        return self;
    }

判断闭包是否提供

通过if判断

   @Test
    void test1() {
        method() {
            println("Hello")
        }
    }
    def method(closure) {
        if(closure) {   // 判断闭包是否提供了
            closure()
        } else {
            println("use default closure")
        }
    }

闭包委托

this, owner,delegate是闭包的3个属性,用于确定由哪个对象处理该闭包内的方法调用;
一般情况下,delegate会设置owner,但可对其进行修改,发挥闭包的一些其他作用;

    def examingClosure(closure) {
        closure()
    }

    @Test
    void test2() {
        examingClosure() {
            println("First Closure: ")
            println("class is ${getClass().name}")
            println("this is ${this}, super: ${this.getClass().superclass.name}")
            println("owner is ${owner}, super:${owner.getClass().superclass.name}")
            println("delegate is ${delegate}, super: ${delegate.getClass().superclass.name}")
        }
    }

输出如下:

First Closure:
class is book.bibao.Test2$_test2_closure2
this is book.bibao.Test2@59717824, super: java.lang.Object
owner is book.bibao.Test2@59717824, super:java.lang.Object
delegate is book.bibao.Test2@59717824, super: java.lang.Object

从输出可以看到,闭包被创建成了内部类,在这里,this owner delegate 指向同一对象;
闭包内方法调用的顺序为先 this,this无法处理,再 owner,再delegate;

修改闭包delegate例子(这个例不咋的)

  class Handler {
        def f1() { println("f1 of Handler called...") }
        def f2() { println("f2 of Handler called...") }
    }

    class Example {
        def f1() { println("f1 of Example called...") }
        def f2() { println("f2 of Example called...") }

        def foo(closure) {
            closure.delegate = new Handler()        // 设置代理
            closure()
        }
    }

    def f1() {
        println("f1 of Script called...")
    }

    @Test
    void test3() {
        new Example().foo() {
            f1()
            f2()
        }
    }

输出:

f1 of Script called...
f2 of Handler called...

以上代码中,闭包内的方法调用首先被路由到闭包上下文对象this,没找到,则路由到delegate

上下文 with 方法

Groovy支持创建一个上下文(context),在with的作用域内调用的任何方法,都会被定向到该上下文对象,如下:

def list = [1,2]
list.add(1)
list.add(2)
println(list)

// 使用with
list.with {
     add(3)
     add(4)
}
println(list)

list.with {
    println("this is ${this}")       // this is book.bibao.With_Test@71c8becc
    println("own is ${owner}")  // own is book.bibao.With_Test@71c8becc
    println("delegate is ${delegate}") //delegate is [1, 2, 1, 2, 3, 4]
 }

调用with方法时,会将该闭包的delegate属性设置到调用with的对象上,delegate,会负责处理this不处理的方法;

尾递归

递归在输入规模较大的情况下,容易Stack Overflow,通过尾递归,可解决这个问题;

  @Test
    void test4() {
        println(factorial(5,1))  // 调用
    }
    def factorial = { int number, BigInteger theFactorial ->
        number == 1 ? theFactorial :
                factorial.trampoline(number - 1, number * theFactorial)
    }.trampoline()

说明: theFactorial 表示通过此递归计算的部分结果
如果 number 为1,则返回 theFactorial的值作为结果,否则调用 trampoline方法

但是:调用此尾递归是需要传递2个参数,我们可以将第二个参数设置一个默认值,但还是不能确保调用方随便传参,这样,可封装个方法如下:

  // 这样外界就不能修改 theFactorial 了
   def factorial(int n) {
        def inner_factorial
        inner_factorial = { int number, BigInteger theFactorial = 1 ->
            number == 1 ? theFactorial :
                    inner_factorial.trampoline(number - 1, number * theFactorial)
        }.trampoline()
        inner_factorial(n)
    }

?问题,如果使用尾递归实现汉诺塔程序?

def hannuo(n, from, to, helper) {
        if (n == 0) {
            return
        } else {
            hannuo(n - 1, from, helper, to)  // 把n-1个从from挪到to,借助helper
            print("$from->$to,")             // 把第n个盘子直接从from挪到to
            hannuo(n - 1, helper, to, from)  // //把n-1个从helper挪到from上,借助to
        }
    }

尾递归使用例子

卖杆问题(《Groovy程序设计》一书中的例子)
不同长度的杆,零售价格不一样,求如何分割杆?

你可能感兴趣的:(Groovy 闭包)