Groovy的Closure(闭包)你学懂了吗?

闭包简介

Groovy中的闭包可以理解为是包装成对象的一段代码,你可以像对待一个普通对象一样看待它,也可以将它看作一个方法,它可以接受参数也有返回值。Groovy的语法很简洁,导致闭包的一些逻辑比较难以理解。使用闭包有两个比较突出的优势:一是可以方便的操作集合;二是对资源的使用更安全,比如操作文件的时候不用再去但因有没有关闭文件等问题。

声明闭包

简单声明

闭包的格式主要是:在方法调用之后,使用大括号将代码片段包起来,括号里面包含参数和闭包主体(代码逻辑),他们之间以箭头分隔。当闭包的参数只有一个的时候,参数可以用关键字it代替,且不需要声明,如下两种方式声明闭包是等价的:

def log = ''
        (1..10).each { counter ->
            log = log + counter
        }
        assert log == '12345678910'
def log1 = ''
        (1..10).each {//it的声明可以省略
            log1 = log1 + it
        }
        assert log1 == '12345678910'

将闭包赋值给变量

另一种声明闭包的方法就是将其直接赋值给一个变量,如下所示,将闭包赋值给了变量printer:

def printer = { line -> println line }

除了这样直接赋值给变量,也可以将闭包赋值给一个方法的返回值:

    def getPrinter() {
        return { line -> println line }
    }

将方法引用为闭包

可以将一个方法的代码逻辑直接因为为一个闭包,然后再对其使用一些闭包的方法,将方法应用为闭包需要使用操作符".&",如下所示:

  1. SizeFilter这个类定义了一个limit的全局变量,包含了方法sizeUpTo。
class SizeFilter {
    Integer limit

    def sizeUpTo(String value) {
        return value.size() <= limit//返回size小于等于limit的value
    }
}
  1. class Filter的filter6和filter5是class SizeFilter的两个实例。
  2. 定义闭包sizeUpTo6,是将SizeFilter里的方法sizeUpTo直接引用为闭包。
  3. 定义一个list words
  4. wordSizeUpTo6是对闭包sizeUpTo6的操作,知道word list中size下雨等于6的word
  5. wordSizeUpTo5中,没有定一个闭包,而是直接将filter5.&sizeUpTo直接传入find方法中进行操作,这也是可以的,因为filter5.&sizeUpTo也是一个闭包。
class Filter {
    SizeFilter filter6 = new SizeFilter(limit: 6)
    SizeFilter filter5 = new SizeFilter(limit: 5)
    Closure sizeUpTo6 = filter6.&sizeUpTo//引用方法sizeUpTo作为闭包
    def words = ['long string', 'medium', 'short', 'tiny']

    @Test
    void wordSizeUpTo6() {
        assert words.find(sizeUpTo6) == "medium"//找到第一个word size小于或等于6的
        assert words.findAll(sizeUpTo6) == ['medium', 'short', 'tiny']//找到所有size小于或等于6的word
    }

    @Test
    void wordSizeUpTo5() {
        assert words.find(filter5.&sizeUpTo) == "short"//找到第一个size小于或等于5的word
        assert words.findAll(filter5.&sizeUpTo) == ['short', 'tiny']//找到所有size小于或等于5的word
    }
}

使用闭包

调用闭包

调用闭包我们可以将其看作是调用一个方法,传入其需要参数即可。如下所示,x.closure()或x.closure.call()都是可以的:

    def adder = { x, y -> return x + y }//声明闭包

    @Test
    void callAdder() {
        assert adder(3, 7) == 10//调用闭包,传入参数(x,y)
        assert adder.call(3, 9) == 12
    }

下面给一个更复杂的例子,在方法里面来调用闭包。

def benchmark(int repeat, Closure worker) {//定义闭包worker作为方法参数传入
        def start = System.nanoTime()//记录开始时间
        repeat.times {//times表示多次(repeat)执行闭包
            worker(it)//it表示执行次数,从0开始,这里的it是可以免声明的
        }
        def stop = System.nanoTime()//记录结束时间
        return stop - start//返回执行时间
    }

    @Test
    void callBenchmark() {//调用benchMark方法
        def slow = benchmark(10000) { it / 2 }//repeat=10000,worker={it / 2}
        def fast = benchmark(10000) { it.intdiv(2) }//repeat=10000,worker={it.intdiv(2)}
        assert fast * 2 < slow
    }

闭包的其他使用方法

我们常常使用闭包的就是它的声明和调用,但是它还有一些其他的使用场景。

处理参数个数和类型

上面使用闭包的例子都是传入一个参数,其实闭包也可以传入多个参数,比如和map操作的时候,可以传入key和value,然后闭包根据你传入的参数个数、类型来调整其行为。那我们也可以使用getMaximumNumberOfParameters和getParameterTypes方法来检索预期的参数个数和类型。如下所示:

    def numParams(Closure closure) {
        closure.getMaximumNumberOfParameters()
    }

    @Test
    void callNumParams() {
        def numOfParams = numParams { one -> }
        assert numOfParams == 1
        assert numParams { one, two -> } == 2
    }
def paramTypes(Closure closure) {
        closure.getParameterTypes()
    }

    @Test
    void callParamTypes() {
        assert paramTypes { String s -> } == [String]
        assert paramTypes { Number n, Date d, String s -> } == [Number, Date, String]
    }

如何使用Curry?

Curry是以人名(Haskell Brooks Curry )命名的技术,主要思想是:通过固定一些入餐的值将有多个参数的方法转换成只有少数几个甚至一个参数的方法。
一个简单的curry方法的示例如下:

        def mult = { x, y -> return x * y }//声明一个闭包
        def twoTimes = mult.curry(2)//一个新的闭包,赋给了第一个参数(默认)值为2
        assert twoTimes(5) == 10//一个新的闭包,只需要一个参数(第二个参数)y。

给指定的参数赋值,以上示例使用的默认给左边第一个参数赋值,也有其他方法给指定的参数赋值,比如rcurry(给最右边的第一个参数赋值),ncurry(给第n个参数赋值),lcurry(明确指定赋值给左边第一个参数)。如下所示:

        def mult = { x, y, z -> return x * y + z }
        def twoTimes = mult.rcurry(2)//一个新的闭包,赋给了z值为2
        assert twoTimes(3, 4) == 14
        def mult = { x, y, z, m, n -> return x * y + z / m + n }
        def twoTimes = mult.ncurry(0, 2)//一个新的闭包,赋给了x值为2(n从0到4)
        assert twoTimes(3, 4, 2, 6) == 14

多个函数组合

可以将多个函数组合起来,合并成一个函数。如下示例:

        def add = { a -> a + 5 }
        def mult = { a -> a * 4 }
        def rightShift = add >> mult//先+再*
        def leftShift = add << mult//先*再+
        assert rightShift(3) == 32
        assert leftShift(3) == 17

闭包返回值

结束返回

结束返回的意思就是闭包逻辑执行到最后一句的时候返回结果,return关键字可以省略,如下:

        [1, 2, 3].collect { it * 2 }
        [1, 2, 3].collect { return it * 2 }

结束前返回

可以在闭包运算结束前就返回,如下所示:

        [1, 2, 3].collect {
            if (it % 2 == 0) return it * 2//if部分返回值
            return it//else部分返回值
        }

猜你喜欢

转载自blog.csdn.net/qunyaoaiziji/article/details/106135662