Groovy中封闭
1. 概述
在这个介绍性教程中,我们将探讨Groovy 中的闭包概念,这是这种动态且强大的 JVM 语言的一个关键特性。
许多其他语言,包括 Javascript 和 Python,都支持闭包的概念。然而,闭包的特性和功能因语言而异。
我们将涉及 Groovy 闭包的关键方面,并展示如何使用它们的示例。
2. 什么是闭包?
闭包 是匿名的代码块。在 Groovy 中,它是**Closure 类的一个实例**。闭包可以接受 0 个或多个参数并且总是返回一个值。
此外,闭包可以访问其范围之外的周围变量,并在执行期间使用它们——连同它的局部变量。
此外,我们可以将闭包分配给变量或将其作为参数传递给方法。因此,闭包提供了延迟执行的功能。
3. 关闭声明
**Groovy 闭包包含参数、箭头 -> 和要执行的代码。**参数是可选的,并且在提供时以逗号分隔。
3.1. 基本声明
def printWelcome = {
println "Welcome to Closures!"
}
在这里,闭包printWelcome在调用时会打印一条语句。现在,让我们写一个一元闭包的快速示例:
def print = { name ->
println name
}
在这里,闭包print接受一个参数 - name - 并在调用时打印它。
由于闭包的定义看起来类似于方法,让我们比较一下:
def formatToLowerCase(name) {
return name.toLowerCase()
}
def formatToLowerCaseClosure = { name ->
return name.toLowerCase()
}
在这里,方法和相应的闭包行为相似。但是,闭包和方法之间存在细微差别,我们将在后面的闭包与方法部分讨论。
3.2. 执行
我们可以通过两种方式执行闭包——我们可以像调用任何其他方法一样调用它,或者我们可以使用call方法。
例如,作为常规方法:
print("Hello! Closure")
formatToLowerCaseClosure("Hello! Closure")
并使用call方法执行:
print.call("Hello! Closure")
formatToLowerCaseClosure.call("Hello! Closure")
4. 参数
Groovy 闭包的参数类似于常规方法的参数。
4.1. 隐式参数
我们可以定义一个不带参数的一元闭包,因为**当没有定义参数时,Groovy 假定一个名为“ it”的隐式参数:
def greet = {
return "Hello! ${it}"
}
assert greet("Alex") == "Hello! Alex"
4.2. 多个参数
这是一个闭包,它接受两个参数并返回它们相乘的结果:
def multiply = { x, y ->
return x*y
}
assert multiply(2, 4) == 8
4.3. 参数类型
在到目前为止的示例中,我们的参数没有提供任何类型。我们还可以设置闭包参数的类型。例如,让我们重写multiply方法以考虑其他操作:
def calculate = {int x, int y, String operation ->
def result = 0
switch(operation) {
case "ADD":
result = x+y
break
case "SUB":
result = x-y
break
case "MUL":
result = x*y
break
case "DIV":
result = x/y
break
}
return result
}
assert calculate(12, 4, "ADD") == 16
assert calculate(43, 8, "DIV") == 5.375
4.4. 可变参数
我们可以在闭包中声明可变数量的参数,类似于常规方法。例如:
def addAll = { int... args ->
return args.sum()
}
assert addAll(12, 10, 14) == 36
5. 闭包作为论据
我们可以将闭包作为参数传递给常规的 Groovy 方法。这允许方法调用我们的闭包来完成它的任务,允许我们自定义它的行为。
让我们讨论一个简单的用例:计算常规数字的体积。
在此示例中,体积定义为面积乘以高度。但是,对于不同的形状,面积的计算可能会有所不同。
因此,我们将编写volume方法,它以闭包areaCalculator作为参数,并在调用期间传递面积计算的实现:
def volume(Closure areaCalculator, int... dimensions) {
if(dimensions.size() == 3) {
return areaCalculator(dimensions[0], dimensions[1]) * dimensions[2]
} else if(dimensions.size() == 2) {
return areaCalculator(dimensions[0]) * dimensions[1]
} else if(dimensions.size() == 1) {
return areaCalculator(dimensions[0]) * dimensions[0]
}
}
assert volume({ l, b -> return l*b }, 12, 6, 10) == 720
让我们使用相同的方法找到一个圆锥体的体积:
assert volume({ radius -> return Math.PI*radius*radius/3 }, 5, 10) == Math.PI * 250
6. 嵌套闭包
我们可以在闭包内声明和调用闭包。
例如,让我们在已经讨论过的calculate闭包中添加一个日志记录功能:
def calculate = {int x, int y, String operation ->
def log = {
println "Performing $it"
}
def result = 0
switch(operation) {
case "ADD":
log("Addition")
result = x+y
break
case "SUB":
log("Subtraction")
result = x-y
break
case "MUL":
log("Multiplication")
result = x*y
break
case "DIV":
log("Division")
result = x/y
break
}
return result
}
7. 字符串的惰性求值
Groovy String通常在创建时进行评估和插值。例如:
def name = "Samwell"
def welcomeMsg = "Welcome! $name"
assert welcomeMsg == "Welcome! Samwell"
即使我们修改name变量的值,welcomeMsg也不会改变:
name = "Tarly"
assert welcomeMsg != "Welcome! Tarly"
闭包插值允许我们提供对String 的惰性求值,从它们周围的当前值重新计算。例如:
def fullName = "Tarly Samson"
def greetStr = "Hello! ${-> fullName}"
assert greetStr == "Hello! Tarly Samson"
只有这一次,更改变量也会影响内插字符串的值:
fullName = "Jon Smith"
assert greetStr == "Hello! Jon Smith"
8. 集合中的闭包
Groovy 集合在其许多 API 中使用闭包。例如,让我们定义一个项目列表并使用具有隐式参数的一元闭包each打印它们:
def list = [10, 11, 12, 13, 14, true, false, "BUNTHER"]
list.each {
println it
}
assert [13, 14] == list.findAll{ it instanceof Integer && it >= 13 }
通常,基于某些标准,我们可能需要从地图创建列表。例如:
def map = [1:10, 2:30, 4:5]
assert [10, 60, 20] == map.collect{it.key * it.value}
9. 闭包与方法
到目前为止,我们已经看到了闭包的语法、执行和参数,它们与方法非常相似。现在让我们比较闭包和方法。
与常规的 Groovy 方法不同:
- 我们可以将closure作为参数传递给方法
- 一元闭包可以使用隐式it参数
- 我们可以将closure分配给一个变量并在以后执行它,可以作为方法或call
- Groovy 在运行时确定闭包的返回类型
- 我们可以在闭包内声明和调用闭包
- 闭包总是返回一个值
因此,闭包比常规方法更有优势,并且是 Groovy 的一个强大功能。