1. Overview
1.概述
In this Introductory tutorial, we’ll explore the concept of closures in Groovy, a key feature of this dynamic and powerful JVM language.
在本介绍性教程中,我们将探讨Groovy中的闭包概念,这是这种动态和强大的JVM语言的一个关键特性。
Many other languages, including Javascript and Python, support the concept of closures. However, the characteristics and functioning of closures vary from language to language.
许多其他语言,包括Javascript和Python,都支持闭包的概念。然而,闭包的特点和功能因语言不同而不同。
We’ll touch on key aspects of Groovy closures, showing examples of how they are used along the way.
我们将触及Groovy闭包的关键方面,沿途展示如何使用闭包的例子。
2. What Is a Closure?
2.什么是闭合?
A closure is an anonymous block of code. In Groovy, it is an instance of the Closure class. Closures can take 0 or more parameters and always return a value.
closure是一个匿名的代码块。在Groovy中,它是Closure类的一个实例。闭包可以接受0个或更多的参数,并且总是返回一个值。
Additionally, a closure may access surrounding variables outside its scope and use them — along with its local variables — during execution.
此外,一个闭包可以访问其范围之外的周围变量,并在执行过程中使用这些变量–连同其本地变量一起。
Furthermore, we can assign a closure to a variable or pass it as a parameter to a method. Therefore, a closure provides functionality for delayed execution.
此外,我们可以将一个闭包分配给一个变量,或者将其作为参数传递给一个方法。因此,闭包提供了延迟执行的功能。
3. Closure Declaration
3.结业声明
A Groovy Closure contains parameters, the arrow ->, and the code to execute. Parameters are optional and, when provided, are comma-separated.
一个Groovy Closure包含参数、箭头->和要执行的代码。参数是可选的,当提供时,是以逗号分隔的。
3.1. Basic Declaration
3.1.基本声明
def printWelcome = {
println "Welcome to Closures!"
}
Here, the closure printWelcome prints a statement when invoked. Now, let’s write a quick example of a unary closure:
在这里,闭包printWelcome在被调用时打印了一条语句。现在,让我们来写一个快速的单数闭包的例子。
def print = { name ->
println name
}
Here, the closure print takes one parameter — name — and prints it when invoked.
这里,闭包print接受一个参数–name–并在调用时打印出来。
Since the definition of a closure looks similar to a method, let’s compare them:
既然闭包的定义看起来与方法相似,我们来比较一下。
def formatToLowerCase(name) {
return name.toLowerCase()
}
def formatToLowerCaseClosure = { name ->
return name.toLowerCase()
}
Here, the method and the corresponding closure behave similarly. However, there are subtle differences between a closure and a method, which we’ll discuss later in the Closures vs Methods section.
在这里,方法和相应的闭包的行为是相似的。然而,闭包和方法之间有一些微妙的区别,我们将在后面的闭包与方法部分讨论。
3.2. Execution
3.2.执行
We can execute a closure in two ways — we can invoke it like it were any other method, or we can use the call method.
我们可以通过两种方式执行一个闭包–我们可以像其他方法一样调用它,或者使用call方法。
For instance, as a regular method:
例如,作为一种常规方法。
print("Hello! Closure")
formatToLowerCaseClosure("Hello! Closure")
And executing with the call method:
并用call方法执行。
print.call("Hello! Closure")
formatToLowerCaseClosure.call("Hello! Closure")
4. Parameters
4.参数
The parameters of Groovy closures are similar to those of regular methods.
Groovy闭包的参数与普通方法的参数类似。
4.1. Implicit Parameter
4.1.隐性参数
We can define a unary closure without a parameter because when parameters are not defined, Groovy assumes an implicit parameter named “it”:
我们可以在没有参数的情况下定义一个单数闭包,因为当没有定义参数时,Groovy会假设一个名为”it “的隐含参数。
def greet = {
return "Hello! ${it}"
}
assert greet("Alex") == "Hello! Alex"
4.2. Multiple Parameters
4.2.多个参数
Here’s a closure that takes two parameters and returns the result of multiplying them:
这里有一个闭包,它接收两个参数并返回它们相乘的结果。
def multiply = { x, y ->
return x*y
}
assert multiply(2, 4) == 8
4.3. Parameter Types
4.3 参数类型
In the examples so far, there has been no type provided with our parameters. We can also set the type of closure parameters. For instance, let’s rewrite the multiply method to consider other operations:
在到目前为止的例子中,没有为我们的参数提供类型。我们也可以设置封闭参数的类型。例如,让我们重写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. Varargs
4.4 Varargs
We can declare a variable number of arguments in closures, similar to regular methods. For example:
我们可以在闭包中声明可变数量的参数,与普通方法类似。比如说
def addAll = { int... args ->
return args.sum()
}
assert addAll(12, 10, 14) == 36
5. A Closure as an Argument
5.作为论据的结尾
We can pass a Closure as an argument to a regular Groovy method. This allows the method to call our closure to complete its task, allowing us to customize its behavior.
我们可以将一个Closure作为参数传递给一个普通的Groovy方法。这允许该方法调用我们的闭包来完成其任务,允许我们自定义其行为。
Let’s discuss a simple use-case: the calculation of the volume of regular figures.
让我们来讨论一个简单的用例:计算规则图形的体积。
In this example, the volume is defined as area multiplied by height. However, calculation of area can vary for different shapes.
在这个例子中,体积被定义为面积乘以高度。然而,对于不同的形状,面积的计算可以有所不同。
Therefore, we’ll write the volume method, which takes a closure areaCalculator as an argument, and we’ll pass the implementation of the area calculation during invocation:
因此,我们将编写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
Let’s find a volume of a cone using the same method:
让我们用同样的方法求一个圆锥体的体积。
assert volume({ radius -> return Math.PI*radius*radius/3 }, 5, 10) == Math.PI * 250
6. Nested Closures
6.嵌套闭合
We can declare and invoke closures inside a closure.
我们可以在一个闭包内声明和调用闭包。
For instance, let’s add a logging ability to the already discussed calculate closure:
例如,让我们为已经讨论过的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. Lazy Evaluation of Strings
7.字符串的懒惰评估
Groovy Strings are usually evaluated and interpolated at the time of creation. For instance:
Groovy Strings通常在创建时被评估和插值。比如说。
def name = "Samwell"
def welcomeMsg = "Welcome! $name"
assert welcomeMsg == "Welcome! Samwell"
Even if we modify the value of the name variable, the welcomeMsg is not going to change:
即使我们修改 name变量的值,welcomeMsg也不会改变。
name = "Tarly"
assert welcomeMsg != "Welcome! Tarly"
Closure interpolation allows us to provide lazy evaluation of Strings, recalculated from the current values around them. For example:
闭合插值允许我们提供对Strings的懒惰评估,从它们周围的当前值重新计算。比如说。
def fullName = "Tarly Samson"
def greetStr = "Hello! ${-> fullName}"
assert greetStr == "Hello! Tarly Samson"
Only this time, changing the variable affects the interpolated string’s value as well:
只是这一次,改变变量也会影响到插值的字符串的值。
fullName = "Jon Smith"
assert greetStr == "Hello! Jon Smith"
8. Closures in Collections
8.收藏中的关闭
Groovy Collections use closures in many of their APIs. For example, let’s define a list of items and print them using the unary closure each, which has an implicit parameter:
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 }
Often, based on some criterion, we may need to create a list from a map. For instance:
通常,根据一些标准,我们可能需要从地图上创建一个列表。比如说。
def map = [1:10, 2:30, 4:5]
assert [10, 60, 20] == map.collect{it.key * it.value}
9. Closures vs Methods
9.封闭与方法
So far, we’ve seen the syntax, execution, and parameters of closures, which are fairly similar to methods. Let’s now compare closures with methods.
到目前为止,我们已经看到了闭包的语法、执行和参数,它们与方法相当类似。现在让我们来比较一下闭包和方法。
Unlike a regular Groovy method:
与普通的Groovy方法不同。
- We can pass a Closure as an argument to a method
- Unary closures can use the implicit it parameter
- We can assign a Closure to a variable and execute it later, either as a method or with call
- Groovy determines the return type of the closures at runtime
- We can declare and invoke closures inside a closure
- Closures always return a value
Hence, closures have benefits over regular methods and are a powerful feature of Groovy.
因此,闭包比普通方法有好处,是Groovy的一个强大功能。
10. Conclusion
10.结语
In this article, we’ve seen how to create closures in Groovy and explored how they are used.
在这篇文章中,我们已经看到了如何在Groovy中创建闭包,并探讨了闭包的使用方法。
Closures provide an effective way to inject functionality into objects and methods for delayed execution.
闭包提供了一种有效的方式,将功能注入对象和方法中,以便延迟执行。
As always, the code and unit tests from this article are available over on GitHub.
一如既往,本文中的代码和单元测试可在GitHub上获得。