Groovy - Closures



A closure is a short anonymous block of code. It just normally spans a few lines of code. A method can even take the block of code as a parameter. They are anonymous in nature.

Following is an example of a simple closure and what it looks like.

class Example {
   static void main(String[] args) {
      def clos = {println "Hello World"};
      clos.call();
   } 
}

In the above example, the code line - {println "Hello World"} is known as a closure. The code block referenced by this identifier can be executed with the call statement.

When we run the above program, we will get the following result −

Hello World

Formal parameters in closures

Closures can also contain formal parameters to make them more useful just like methods in Groovy.

class Example {
   static void main(String[] args) {
      def clos = {param->println "Hello ${param}"};
      clos.call("World");
   } 
}

In the above code example, notice the use of the ${param } which causes the closure to take a parameter. When calling the closure via the clos.call statement we now have the option to pass a parameter to the closure.

When we run the above program, we will get the following result −

Hello World

The next illustration repeats the previous example and produces the same result, but shows that an implicit single parameter referred to as it can be used. Here ‘it’ is a keyword in Groovy.

class Example {
   static void main(String[] args) {
      def clos = {println "Hello ${it}"};
      clos.call("World");
   } 
}

When we run the above program, we will get the following result −

Hello World

Closures and Variables

More formally, closures can refer to variables at the time the closure is defined. Following is an example of how this can be achieved.

class Example {     
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = {param -> println "${str1} ${param}"}
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
   } 
}

In the above example, in addition to passing a parameter to the closure, we are also defining a variable called str1. The closure also takes on the variable along with the parameter.

When we run the above program, we will get the following result −

Hello World 
Welcome World

Using Closures in Methods

Closures can also be used as parameters to methods. In Groovy, a lot of the inbuilt methods for data types such as Lists and collections have closures as a parameter type.

The following example shows how a closure can be sent to a method as a parameter.

class Example { 
   def static Display(clo) {
      // This time the $param parameter gets replaced by the string "Inner"         
      clo.call("Inner");
   } 
	
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = { param -> println "${str1} ${param}" }
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
		
      // Passing our closure to a method
      Example.Display(clos);
   } 
}

In the above example,

  • We are defining a static method called Display which takes a closure as an argument.

  • We are then defining a closure in our main method and passing it to our Display method as a parameter.

When we run the above program, we will get the following result −

Hello World 
Welcome World 
Welcome Inner

Closures in Collections and String

Several List, Map, and String methods accept a closure as an argument. Let’s look at example of how closures can be used in these data types.

Using Closures with Lists

The following example shows how closures can be used with Lists. In the following example we are first defining a simple list of values. The list collection type then defines a function called .each. This function takes on a closure as a parameter and applies the closure to each element of the list.

class Example {
   static void main(String[] args) {
      def lst = [11, 12, 13, 14];
      lst.each {println it}
   } 
}

When we run the above program, we will get the following result −

11 
12 
13 
14

Using Closures with Maps

The following example shows how closures can be used with Maps. In the following example we are first defining a simple Map of key value items. The map collection type then defines a function called .each. This function takes on a closure as a parameter and applies the closure to each key-value pair of the map.

class Example {
   static void main(String[] args) {
      def mp = ["TopicName" : "Maps", "TopicDescription" : "Methods in Maps"]             
      mp.each {println it}
      mp.each {println "${it.key} maps to: ${it.value}"}
   } 
}

When we run the above program, we will get the following result −

TopicName = Maps 
TopicDescription = Methods in Maps 
TopicName maps to: Maps 
TopicDescription maps to: Methods in Maps

Often, we may wish to iterate across the members of a collection and apply some logic only when the element meets some criterion. This is readily handled with a conditional statement in the closure.

class Example {
   static void main(String[] args) {
      def lst = [1,2,3,4];
      lst.each {println it}
      println("The list will only display those numbers which are divisible by 2")
      lst.each{num -> if(num % 2 == 0) println num}
   } 
}

The above example shows the conditional if(num % 2 == 0) expression being used in the closure which is used to check if each item in the list is divisible by 2.

When we run the above program, we will get the following result −

1 
2 
3 
4 
The list will only display those numbers which are divisible by 2.
2 
4 

Methods used with Closures

The closures themselves provide some methods.

Sr.No. Methods & Description
1 find()

The find method finds the first value in a collection that matches some criterion.

2 findAll()

It finds all values in the receiving object matching the closure condition.

3 any() & every()

Method any iterates through each element of a collection checking whether a Boolean predicate is valid for at least one element.

4 collect()

The method collect iterates through a collection, converting each element into a new value using the closure as the transformer.

Advertisements