Groovy 闭包
最后修改日期:2025 年 3 月 22 日
Groovy 中的闭包是动态的、可重用的代码块,你可以将它们分配给变量、传递或按需执行。它们比 Java 的 Lambda 表达式更具通用性,将函数式编程与 Groovy 的富有表现力的语法融为一体。本教程通过实际的、真实世界的示例,探讨如何创建和使用闭包。
定义闭包
闭包是用花括号 {}
雕刻而成的,可以选择性地接受参数并返回值。作为一等公民,闭包可以像任何变量一样存储、共享和调用,从而解锁了灵活逻辑的世界。
def welcome = { name -> "Welcome, $name, to our site!" } println welcome("Alice")
这里,welcome
是一个闭包,它通过 name
向用户致以问候,并使用 Groovy 的字符串插值来生成一个完美的问候语。想象一下在 Web 应用程序中,用最少的麻烦为每个访问者个性化登录横幅。
将闭包作为方法参数
当将闭包传递给方法时,它们会大放异彩,将自定义行为注入可重用的函数中。这使得你的代码具有适应性,可以让你在不重写核心结构的情况下交换逻辑。
def scheduleTasks(int count, Closure task) { count.times { task(it + 1) } } scheduleTasks(3) { taskNum -> println "Task $taskNum scheduled for today" }
scheduleTasks
接受一个计数和一个闭包,并通过 times
对每次迭代执行该闭包。这里,它安排了编号的任务,模仿了一个待办事项应用程序,其中闭包定义了每个任务的内容——灵活而直接。
具有多个参数的闭包
闭包可以处理多个参数,这对于需要一个以上输入的运算非常有用,从计算到数据转换。
def calculateTax = { price, rate -> price * (1 + rate) } println calculateTax(100, 0.08)
calculateTax
函数计算带税的总价,它接受 price
和 rate
。这可以为电子商务结账提供支持,快速计算税后价格,而不会使代码因完整的函数定义而混乱。
将闭包作为返回值
方法可以创建并返回闭包,从而实现能够即时生成定制逻辑的高阶函数——非常适合创建可重用的工具。
def discountFactory(int percent) { { price -> price * (1 - percent / 100) } } def tenOff = discountFactory(10) println tenOff(50)
discountFactory
返回一个应用百分比折扣的闭包。tenOff
提供 10% 的折扣,这对于零售系统很有用,在该系统中,不同的销售活动需要自定义的折扣计算器,而无需重复代码。
闭包和集合
闭包与集合配合得非常好,可以使用简洁、富有表现力的语法简化过滤、转换或聚合数据等任务。
def sales = [25.50, 19.99, 45.00, 10.75, 60.20] def withTax = sales.collect { it * 1.07 } println withTax def highSales = sales.findAll { it > 20 } println highSales
collect
将 7% 的税应用于每笔销售,而 findAll
过滤出超过 20 美元的销售额。设想一下在销售仪表板中,只需少量工作即可处理每日数据或突出显示大型交易。
闭包和作用域
闭包会捕获其周围的作用域,即使在其创建上下文消失后也能访问外部变量。这种“记忆”功能使其成为状态化逻辑的强大工具。
def counter = 0 def increment = { counter++ } increment(); increment() println "Count: $counter"
increment
修改其外部作用域中的 counter
,就像 Web 应用程序中的点击跟踪器一样。每次调用都会更新共享状态,显示闭包如何轻松地连接局部数据和持久化数据。
Currying 闭包
Currying 将一个多参数闭包转换为一个具有预设参数的专用版本,通过预配置的逻辑简化重复任务。
def logEvent = { level, msg -> println "[$level] $msg" } def logInfo = logEvent.curry("INFO") logInfo("User logged in")
logEvent
使用级别和消息进行日志记录;logInfo
将级别锁定为“INFO”。这适用于日志级别信息日志很常见的日志记录系统,避免了您每次都重复记录级别。
闭包和委托
闭包可以将方法调用委托给另一个对象,从而动态地将行为路由到指定的“助手”,以实现自适应、上下文驱动的执行。
class Notifier { String notify(String user) { "Alert sent to $user" } } def alert = { notify("Alice") } alert.delegate = new Notifier() alert.resolveStrategy = Closure.DELEGATE_FIRST println alert()
alert
将 notify
委托给一个 Notifier
实例,该实例由 delegate
和 DELEGATE_FIRST
设置。这可能会在监控系统中触发通知,允许闭包动态地访问外部逻辑。
用于验证的闭包
闭包可以强制执行规则,使其非常适合在处理数据之前验证数据,并且可以使用针对特定需求的重用逻辑。
def isValidEmail = { email -> email =~ /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ } def email = "user@domain.com" println isValidEmail(email) ? "Valid email" : "Invalid email"
isValidEmail
检查电子邮件是否与正则表达式匹配,并返回一个布尔值。这在注册表单中非常有用,该闭包在提交前确保电子邮件有效性,从而使验证保持简洁且可移植。
用于事件处理的闭包
闭包非常适合作为事件处理程序,以自定义操作响应触发器,非常适合 UI 或系统交互。
def onClick = { event -> println "Button clicked at ${new Date()} by $event.user" } def simulateClick = { Closure handler -> handler([user: "Alice"]) } simulateClick(onClick)
onClick
使用时间戳和用户记录按钮点击事件,该事件会传递给 simulateClick
。这模拟了一个 GUI 框架,其中闭包处理用户操作,无缝地结合了时间和上下文。
闭包组合
闭包可以组合,通过链式转换创建强大、模块化的数据管道,例如逐步处理用户输入。
def trim = { it.trim() } def capitalize = { it.capitalize() } def formatName = trim >> capitalize println formatName(" alice ")
formatName
使用 >>
运算符结合 trim
和 capitalize
,用于清理和格式化名称。这对于表单处理器来说非常理想,它展示了闭包如何整洁地堆叠操作。
使用闭包的最佳实践
- 捕获可重用逻辑: 将常用任务包装在闭包中,以便在整个应用程序中轻松重用,例如格式化或验证。
- 掌握集合: 利用
collect
或findAll
等方法以及闭包来优雅地处理数据。 - 通过 Currying 简化: 通过 Currying 预设参数,以从通用闭包创建专用工具。
- 明智地委托: 使用委托将闭包与对象连接起来,在不硬编码依赖项的情况下调整行为。
来源
本教程深入探讨了 Groovy 闭包,揭示了它们作为灵活、函数式代码块的力量。通过实际示例,我们看到了它们如何以优雅和轻松的方式转变编码任务,从数据处理到事件管理。
作者
列出 所有 Groovy 教程。