谈谈Groovy闭包
A closure is a function with variables bound to a context or environment in which it executes.
概述###
闭包和元编程是Groovy语言的两大精髓。Groovy的闭包大大简化了容器的遍历,提升了代码的可扩展性,使代码更加简洁优雅。闭包在Groovy编程中几乎无处不在。
闭包就是一个闭合代码块,可以引用传入的变量。在 “Python使用闭包结合配置自动生成函数” 一文中,讲解了闭包的基本概念及如何使用闭包批量生产函数。本文谈谈Groovy的闭包及应用。
概念###
定义闭包####
闭包在Groovy 的类型是 groovy.lang.Closure , 如下代码创建了一个使用 closure 来处理 Range [1,2,...,num] 的函数:
def static funcWithClosure(int num, final Closure closure) {
(1..num).collect { closure(it) }
}
使用该函数的代码如下:
println funcWithClosure(5, {x -> x*x})
如果闭包是最后一个参数,还可以写成:
println funcWithClosure(5) { x -> x * 2 }
闭包与函数的区别####
有童鞋可能疑惑:闭包的形式很像函数,它与函数有什么区别呢?
我们知道函数执行完成后,其内部变量会全部销毁,但闭包不会。闭包引用的外部变量会一直保存。闭包引用的外部变量具有“累积效应”,而函数没有。看下面一段代码:
def static add(num) {
def sum = 0
sum += num
return sum
}
def static addByClosure(init) {
def addInner = {
inc ->
init += inc
init
}
return addInner
}
println "one call: ${add(5)}" // one call: 5
println "two call: ${add(5)}" // two call: 5
def addClosure = addByClosure(0)
println "one call: ${addClosure(5)}" // one call: 5
println "two call: ${addClosure(5)}" // two call: 10
第一个函数没有什么特别,进进出出,每次运行得到相同结果。 第二个函数,返回了一个闭包,这个闭包保存了传入的初始值,并且这个闭包能够将初始值加上后续传入给它的参数。划重点: 这里的初始值 init 是函数传入的参数,当这个参数被闭包引用后,它在函数第一次执行完成后值并没有被销毁,而是保存下来。
柯里化####
在 “函数柯里化(Currying)示例” 一文中讲述了函数柯里化的概念及Scala示例。Groovy 也提供了 curry 函数来支持 Curry.
如下所示,计算 sumPower(num, p) = 1^p + 2^p + ... + num^p 。
// sum(n, m) = 1^m + 2^m + ... + n^m
def sumPower = {
power, num ->
def sum = 0
1.upto(num) {
sum += Math.pow(it, power)
}
sum
}
def sumPower_2 = sumPower.curry(2)
println "1^2 + 2^2 + 3^2 = ${sumPower_2(3)}"
println "1^2 + 2^2 + 3^2 + 4^2 = ${sumPower_2(4)}"
def sumPower_3 = sumPower.curry(3)
println "1^3 + 2^3 + 3^3 = ${sumPower_3(3)}"
println "1^3 + 2^3 + 3^3 + 4^3 = ${sumPower_3(4)}"
sumPower.curry(2) 先赋值 power = 2 带入闭包块,得到一个闭包:
def sumPower_2Explict = {
num ->
def sum = 0
1.upto(num) {
sum += Math.pow(it, 2)
}
sum
}
再分别调用 sumPower_2Explict(3) = 14.0 , sumPower_2Explict(4) = 30.0
柯里化使得闭包的威力更加强大了。 它是一个强大的函数工厂,可以批量生产大量有用的函数。
应用###
闭包可以很容易地实现以下功能:
- 遍历容器
- 实现模板方法模式,可用于资源释放等
- 代码的可复用和可扩展
- 构建 超轻量级框架
遍历容器####
如下代码所示,分别创建了一个Map, List 和 Range, 然后使用 each 方法遍历。一个闭合代码块,加上一个遍历变量,清晰简单。注意到,如果是一个单循环遍历,可以直接用 it 表示;如果是 Map 遍历,使用 key, value 二元组即可。
class GroovyBasics {
static void main(args) {
def map = ["me":["name": 'qin', "age": 28], "lover":["name": 'ni', "age": 25]]
map.each {
key, value -> println(key+"="+value)
}
def alist = [1,3,5,7,9]
alist.each {
println(it)
}
(1..10).each { println(it) }
def persons = [new Person(["name": 'qin', "age": 28]), new Person(["name": 'ni', "age": 25])]
println persons.collect { it.name }
println persons.find { it.age >=28 }.name
}
}
再看一段代码:
(1..10).groupBy { it % 3 == 0 } .each {
key, value -> println(key.toString()+"="+value)
}
将 [1,10]之间的数按照是否被3除尽分组得到如下结果,使用链式调用连接的两个闭包实现,非常简明。
false=[1, 2, 4, 5, 7, 8, 10]
true=[3, 6, 9]
模板方法模式####
模板方法模式将算法的可变与不可变部分分离出来。 通常遵循如下模式: doCommon1 -> doDiff1 -> ... DoDiff2 -> ... -> DoCommon2 。 Java 实现模板方法模式,通常需要先定义一个抽象类,在抽象类中定义好算法的基本流程,然后定义算法里那些可变的部分,由子类去实现。可参阅:“设计模式之模板方法模式:实现可扩展性设计(Java示例)” 。
使用闭包可以非常轻松地实现模板方法模式,只要将可变部分定义成 闭包即可。
def static templateMethod(list, common1, diff1, diff2, common2) {
common1 list
diff1 list
diff2 list
common2 list
}
def common1 = { list -> list.sort() }
def common2 = { println it }
def diff1 = { list -> list.unique() }
def diff2 = { list -> list }
templateMethod([2,6,1,9,8,2,4,5], common1, diff1, diff2, common2)
可复用与可扩展####
可复用与可扩展性的前提是,将规则、流程中的可变与不可变分离出来,不可变代表着可复用部分,可变代表着可扩展的部分。由于闭包能够很容易地将可变与不可变分离,因此也很有益于实现代码的可复用与可扩展。
小结###
闭包和元编程是Groovy语言的两大精髓。本文讲解了Groovy闭包的定义、与函数的区别、柯里化及在遍历容器、实现模板方法模式等应用。使用闭包可提升代码的可复用和可扩展性,使代码更加简洁优雅,对于提升编程能力非常有益处。
参考文献###
谈谈Groovy闭包的更多相关文章
- Java8-Function使用及Groovy闭包的代码示例
导航 定位 概述 代码示例 Java-Function Groovy闭包 定位 本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场 ...
- Groovy闭包详解
Groovy闭包是一种可执行代码块的方法,闭包也是对象,可以向方法一样传递参数,因为闭包也是对象,因此可以在需要的时候执行,像方法一样闭包可以传递一个或多个参数.闭包最常见的用途就是处理集合,可以遍历 ...
- Java8函数接口实现回调及Groovy闭包的代码示例
本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场景: 主体流程是不变的,变的只是其中要调用的具体方法. 其特征是: Begi ...
- lambda表达式和groovy闭包的区别
groovy定义的闭包是 Closure 的实例,lambda表达式只是在特定的接⼝或者抽象类的匿名实现,他们之间最主要区别闭包可以灵活的配置代理策略⽽labmda表达式不允许
- Groovy闭包
定义 闭包(Closure)是一种数据类型,它代表一段可执行的代码.它可以作为方法的参数,或者返回值,也可以独立运行,定义如下: def xxx = {parameters -> code} ...
- groovy闭包科里化参数
科里化闭包:带有预先绑定形参的闭包.在预先绑定一个形参之后,调用闭包时就不必为这个形参提供实参了.有助于去掉方法调用中的冗余重复. 使用curry方法科里化任意多个参数 使用rcurry方法科里化后面 ...
- Groovy常用编程知识点简明教程
概述 Groovy 是一门基于JVM的动态类型语言,可以与 Java 平台几乎无缝融合(与Java代码一样编译成字节码). 使用 Groovy ,可以增强 Java 应用的灵活性和可扩展性,提升开发效 ...
- Gradle学习之闭包
Gradle中的闭包其实就等同于Groovy中闭包,Groovy是一种jvm语言,语法兼容于java,曾几何时,也在脚本语言中独树一帜,初学Gradle的时候,大家很容易被其语法所迷惑,由于Gradl ...
- 基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现
目标 设计一个轻量级测试用例框架,接口测试编写者只需要编写测试用例相关的内容(入参及结果校验),不需要理会系统的实现,不需要写跟测试校验无关的内容. 思路 测试用例分析 一个用例由以下部分组成: (1 ...
随机推荐
- Linux中的轻量级进程
在Linux中,轻量级进程可以是进程,也可以是线程.我们所说的线程,在Linux中,其实是轻量级进程之间共享代码段,文件描述符,信号处理,全局变量时: 如果不共享,就是我们所说的进程. 进程是资源管理 ...
- webpack安装与配置初学者踩坑篇
webpack是基于nodejs开发出来的前端工具 webpack可以处理js文件的依赖关系,webpack能够处理js的兼容问题,把高级浏览器不识别的语法转换成浏览器正常识别的语法 (jnlp是基于 ...
- sql server外网复制+非默认端口
注意查看服务器名称,这出来的是什么,就必须要在别名上写什么,如果不一样,请参照 :修改计算机名 SELECT @@SERVERNAME as InstalledName, SERVERPROPERTY ...
- (4.5)mysql备份还原——深入解析二进制日志(1)binlog的3种工作模式与配置
(4.5)mysql备份还原——深入解析二进制日志(binlog) 关键词:二进制日志,binlog日志 0.建议 (1)不建议随便去修改binlog格式(数据库级别) (2)binlog日志的清理 ...
- (1.8)mysql中的trace
(1.8)mysql中的trace 以json格式存储
- dedecms自定义表单提交成功后提示信息修改和跳转链接修改
我们在用dedecms自定义表单提交成功后提示信息一般是"Dedecms 提示信息",这个要怎么改成自己想要的文字呢?还有就是提示页停留时间,目前估计就2秒,太快了,要如何设置长点 ...
- MySQL8.0.11 组复制配置
my.cnf [mysql] prompt='node2 [\h] {\u} (\d) > ' # [client] user = sa password = cc.123 port = 22 ...
- 云锁-安全,易用,灵活的许可-Virbox许可管理平台
云许可,助力开发者保护软件财富 Virbox 云锁提供高强度加密及软件许可管理功能 安全,易用,灵活 防破解 云许可安全性体验与硬件锁一致,避免盗版导致损失 加密保护技术:反黑引擎/碎片代码执行/虚拟 ...
- (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
- 【LeetCode每天一题】Add Two Numbers(两链表相加)
You are given two non-empty linked lists representing two non-negative integers. The digits are stor ...