Chapter 5. Groovy Closures
In this chapter, we will focus exclusively on closures. We touched upon closures already in the previous chapter. Now, we will take a close look at them from every angle. Why devote a whole chapter of the book to one aspect of the language? The reason is that closures are the single most important feature of the Groovy language. Closures are the special seasoning that helps Groovy stand out from Java. They are also the single most powerful feature that we will use when implementing DSLs. In this chapter, we will discuss the following topics:
We will start by explaining just what a closure is and how we can define some simple closures in our Groovy code
We will look at how many of the built-in collection methods make use of closures for applying iteration logic, and see how this is implemented by passing a closure as a method parameter
We will look at the various mechanisms for calling closures, and we will take a look under the covers at how Groovy implements its...
Closures are such an unfamiliar concept to begin with that it can be hard to grasp initially. Closures have characteristics that make them look like a method in so far as we can pass parameters to them and they can return a value. However, unlike methods, closures are anonymous. A closure is just a snippet of code that can be assigned to a variable and executed later:
Because closures are anonymous, they can easily be lost or overwritten. In the preceding example, we defined a variable greeter
to contain a closure that prints a greeting. After greeter
is overwritten with an empty closure, any reference to the original closure is lost.
Tip
It's important to remember that greeter
is not the closure. It is a variable that contains a closure, so it can be supplanted at any time.
Given that greeter
is a...
Closures and collection methods
In the last chapter, we encountered Groovy lists and saw some of the iteration functions, such as the each
method:
This looks like it could be a specialized control loop similar to a while
loop. In fact, it is a call to the each
method of Object
. The each
method takes a closure as one of its parameters, and everything between the curly braces {}
defines another anonymous closure.
Closures defined in this way can look quite similar to code blocks, but they are not the same. Code defined in a regular Java or Groovy style code block is executed as soon as it is encountered. With closures, the block of code defined in the curly braces is not executed until the call()
method of the closure is made:
This will print the following:
Let's dig a bit deeper into the structure of each...
Closures as method parameters
We already know that parentheses around method parameters are optional, so the previous call to each
can also be considered equivalent to:
Groovy has special handling for methods whose last parameter is a closure. When invoking these methods, the closure can be defined anonymously after the method call parentheses. So, yet another legitimate way to call the preceding line is:
The general convention is not to use parentheses unless there are parameters in addition to the closure:
The signature of the GDK findIndexOf
method is:
We can define our own methods that accept closures as parameters. The simplest case is a method that accepts only a single closure as...
In our previous examples, we were passing closures to the built-in collection methods. In the examples to date, we have deferred to the collection method to do the closure invocations for us. Let's now look at how we can make a call to the closure ourselves. For the sake of this example, we will ignore the fact that the GDK provides versions of the Thread.start
method that achieves the same thing:
Here we define a subclass of the Java Thread
class, which can be constructed with...
Finding a named closure field
All of the previous techniques for calling a closure rely on us having prior knowledge of a field or variable that contains a closure. This is fine for most straightforward applications of closures, but this book is about developing DSLs, so let's dig a little deeper.
Take the Grails application framework as an example. You can download Grails from http://grails.org/Download. Grails uses closures as a neat way of defining actions for its user interface controllers. No further configuration is required for the Grails runtime to be able to dispatch requests to an action:
We can implement a login action for our user controller in Grails—simply by declaring a closure in the controller
class and assigning it to a field called login
. In the UI, Grails provides tags to automatically create a link that will dispatch to our login action:
In our previous examples, we have made use of the it
keyword. When a closure accepts only a single parameter, we are able to refer to this parameter as it
and are free from having to explicitly define the parameter. The possible syntax definitions for a closure are:
The default case allows any parameters to be passed to the closure:
The closure does not accept any parameters:
The closure can accept one to many parameters with optional type annotations:
The parameter list is a comma-separated list of parameter names with optional type definitions. Closures behave slightly different depending on whether we supply the optional type:
Closure declarations syntax provides no means of defining a return value. Every closure does, however, return a value with each invocation. A closure can have explicit return
statements. If a return
statement is encountered, then the value defined in the return
statement is returned; otherwise, execution continues until the last statement in the closure block:
If no return
statement is encountered, then the value returned by the closure is the result of evaluating the last statement encountered in the closure block. If the last statement has no value, the closure will return null
:
Closures have access to variables in their surrounding scope. These can be local variables or parameters passed to a method inside which the closure is defined. Here, we can access the name
parameter and the local variable salutation
in our closure:
If the closure is defined within a class
method, then the object instance fields are also available to the closure. The field member separator
, shown in the following code, is also accessible within the closure:
In this chapter, we covered closures in some depth. We covered all of the important aspects of working with closures. We explored the various ways to call a closure and the means of passing parameters. We saw how we can pass closures as parameters to methods, and how this construct can allow us to appear to add mini DSL syntax to our code.
Closures are the real "power" feature of Groovy, and they form the basis of most of the DSLs that we will develop later in this book. In the next chapter, we will build on this knowledge of closures and take a look at some more of the power features of the Groovy language, including builders and metaprogramming with the ExpandoMetaClass
classes.