Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Groovy Closures

Save for later
  • 9 min read
  • 16 Sep 2015

article-image

In this article by Fergal Dearle, the author of the book Groovy for Domain-Specific Languages - Second Edition, we will focus exclusively on closures. We will take a close look at them from every angle. 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 the article, 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

A handy reference that you might want to consider having at hand while you read this article is GDK Javadocs, which will give you full class descriptions of all of the Groovy built-in classes, but of particular interest here is groovy.lang.Closure.

(For more resources related to this topic, see here.)

What is a closure

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:

def flintstones = ["Fred","Barney"]

def greeter = { println "Hello, ${it}" }

flintstones.each( greeter )

greeter "Wilma"

greeter = { }

flintstones.each( greeter )

greeter "Wilma"

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.

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.

Because greeter has a dynamic type, we could have assigned any other object to it. All closures are a subclass of the type groovy.lang.Closure. Because groovy.lang is automatically imported, we can refer to Closure as a type within our code. By declaring our closures explicitly as Closure, we cannot accidentally assign a non-closure to them:

Closure greeter = { println it }

For each closure that is declared in our code, Groovy generates a Closure class for us, which is a subclass of groovy.lang.Closure. Our closure object is an instance of this class. Although we cannot predict what exact type of closure is generated, we can rely on it being a subtype of groovy.lang.Closure.

Closures and collection methods

We will encounter Groovy lists and see some of the iteration functions, such as the each method:

def flintstones = ["Fred","Barney"]

 

flintstones.each {

   println "Hello, ${it}"

}

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:

println "one"

def two =

{

println "two"

}

println "three"

two.call()

println "four"

Will print the following:

one

three

two

four

Let's dig a bit deeper into the structure of the each of the calls shown in the preceding code. I refer to each as a call because that's what it is—a method call. Groovy augments the standard JDK with numerous helper methods. This new and improved JDK is referred to as the Groovy JDK, or GDK for short. In the GDK, Groovy adds the each method to the java.lang.Object class. The signature of the each method is as follows:

Object each(Closure closure)

The java.lang.Object class has a number of similar methods such as each, find, every, any, and so on. Because these methods are defined as part of Object, you can call them on any Groovy or Java object. They make little sense on most objects, but they do something sensible if not very useful:

given: "an Integer"

   def number = 1

when: "we call the each method on it"

   number.each { println it }

then: "just the object itself gets passed into the Closure"

   "1" == output()

These methods all have specific implementations for all of the collection types, including arrays, lists, ranges, and maps. So, what is actually happening when we see the call to flintstones.each is that we are calling the list's implementation of the each method. Because each takes a Closure as its last and only parameter, the following code block is interpreted by Groovy as an anonymous Closure object to be passed to the method.

The actual call to the closure passed to each is deferred until the body of the each method itself is called. The closure may be called multiple times—once for every element in the collection.

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:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at AU $19.99/month. Cancel anytime
flintstones.each ({ println "Hello, ${it}")

Groovy has a special handling for methods whose last parameter is a closure. When invoking these methods, the closure can be defined anonymously after the method call parenthesis. So, yet another legitimate way to call the preceding line is:

flintstones.each() { println "hello, ${it}" }

The general convention is not to use parentheses unless there are parameters in addition to the closure:

given:

   def flintstones = ["Fred", "Barney", "Wilma"]

when: "we call findIndexOf passing int and a Closure"

   def result = flintstones.findIndexOf(0) { it == 'Wilma'}

then:

   result == 2

The signature of the GDK findIndexOf method is:

int findIndexOf(int, Closure)

We can define our own methods that accept closures as parameters. The simplest case is a method that accepts only a single closure as a parameter:

def closureMethod(Closure c) {

   c.call()

}

 

when: "we invoke a method that accepts a closure"

   closureMethod {

       println "Closure called"

   }

then: "the Closure passed in was executed"

   "Closure called" == output()

Method parameters as DSL

This is an extremely useful construct when we want to wrap a closure in some other code. Suppose we have some locking and unlocking that needs to occur around the execution of a closure. Rather than the writer of the code to locking via a locking API call, we can implement the locking within a locker method that accepts the closure:

def locked(Closure c) {

   callToLockingMethod()

   c.call()

   callToUnLockingMethod()

}

The effect of this is that whenever we need to execute a locked segment of code, we simply wrap the segment in a locked closure block, as follows:

locked {

   println "Closure called"

}

In a small way, we are already writing a mini DSL when we use these types on constructs. This call to the locked method looks, to all intents and purposes, like a new language construct, that is, a block of code defining the scope of a locking operation.

When writing methods that take other parameters in addition to a closure, we generally leave the Closure argument to last. As already mentioned in the previous section, Groovy has a special syntax handling for these methods, and allows the closure to be defined as a block after the parameter list when calling the method:

def closureMethodInteger(Integer i, Closure c) {

   println "Line $i"

   c.call()

}

 

 

when: "we invoke a method that accepts an Integer and a Closure"

   closureMethodInteger(1) {

       println "Line 2"

   }

then: "the Closure passed in was executed with the parameter"

   """Line 1

Line 2""" == output()

Forwarding parameters

Parameters passed to the method may have no impact on the closure itself, or they may be passed to the closure as a parameter. Methods can accept multiple parameters in addition to the closure. Some may be passed to the closure, while others may not:

def closureMethodString(String s, Closure c) {

   println "Greet someone"

 c.call(s)

}

 

when: "we invoke a method that accepts a String and a Closure"

   closureMethodString("Dolly") { name ->

       println "Hello, $name"

   }

then: "the Closure passed in was executed with the parameter"

   """Greet someone

Hello, Dolly""" == output()

This construct can be used in circumstances where we have a look-up code that needs to be executed before we have access to an object. Say we have customer records that need to be retrieved from a database before we can use them:

def withCustomer (id, Closure c) {

   def cust = getCustomerRecord(id)

   c.call(cust)

}

 

withCustomer(12345) { customer ->

   println "Found customer ${customer.name}"

}

We can write an updateCustomer method that saves the customer record after the closure is invoked, and amend our locked method to implement transaction isolation on the database, as follows:

class Customer {

   String name

}

def locked (Closure c) {

   println "Transaction lock"

   c.call()

   println "Transaction release"

}

 

def update (customer, Closure c) {

   println "Customer name was ${customer.name}"

   c.call(customer)

   println "Customer name is now ${customer.name}"

}

 

def customer = new Customer(name: "Fred")

At this point, we can write code that nests the two method calls by calling update as follows:

locked {

   update(customer) { cust ->

       cust.name = "Barney"

   }

}

This outputs the following result, showing how the update code is wrapped by updateCustomer, which retrieves the customer object and subsequently saves it. The whole operation is wrapped by locked, which includes everything within a transaction:

Transaction lock

Customer name was Fred

Customer name is now Barney

Transaction release

Summary

In this article, we covered closures in some depth. 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.

Resources for Article:


Further resources on this subject: