Closures

A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable. Groovy uses closures to specify a code to be executed each time and adds the extra methods ( each, any, every, find, findAll, collect and so forth) to the collection classes to make them readily available. 

See here for more information on Closures.

On this page:

Defining a closure

Simple declaration: The following example shows a simple closure syntax. When there is only one parameter passed into closure, its declaration is optional.

def log = ''
(1..10).each { counter -> log += counter }
log == "12345678910"

//it variable that needs no declaration
def log = ""
(1..10).each { log += it }
log == "12345678910"

Using assignments for declaration: A second way of declaring a closure is to directly assign it to a variable.

//Assigning a closure to a variable
Closure printer = { line -> println line }
printer instanceof Closure //returns true

//Empty closure
def emptyClosure = {}
emptyClosure instanceof Closure //returns true

//Assignment to the return value of a method
def Closure getPrinter() {
return{ line -> println line }
} 
getPrinter() instanceof Closure //returns true

Referring to methods as closures: Declaring a closure is to reuse something that is already declared a method. Groovy lets you reuse the code you already have in methods but as a closure. 

//Method closure assignment statement
def c = reference.&someMethod

//Different implementations are called based on argument types
class MultiMethodSample {
 int mysteryMethod (String value) {
 return value.length()
 }
 int mysteryMethod (List list) {
 return list.size()
 }
 int mysteryMethod (int x, int y) {
 return x+y
 }
}
MultiMethodSample instance = new MultiMethodSample()
Closure multi = instance.&mysteryMethod

multi("Groovy in JMWE") == 14
multi(["a","Red",3.5]) == 3
multi(5,13) == 18

See below for an example that shows all of these ways of creating and using closures

Map map = ['a':1, 'b':2] 
//Closure passed as parameter directly
map.each{key, value -> 
  map[key] = value * 2  
}
map

//Assign a closure object to a variable and call it
doubler = {key, value -> 
  map[key] = value * 2  
}
map.each(doubler)

//Use of reference.&operator
def doubleMethod(entry){ 
 entry.value = entry.value * 2 
}
doubler = this.&doubleMethod 
map.each(doubler)

Calling a closure

A closure, as an anonymous block of code, can be called like any other method.

def code = { 1234 }
code() == 1234
code.call() == 1234

//int as a parameter
def numberIsOdd = { int i -> i%2 !=0 }
numberIsOdd(3) == true
numberIsOdd.call(4) == false

//Same closure with implicit parameter it
def numberIsOdd = { it%2 !=0 }
numberIsOdd(3) == true
numberIsOdd.call(4) == false

Parameters in a Closure

Parameters of closures follow the same principle as parameters of regular methods: an optional type, a name, an optional default value. They are separated with commas. It is possible for a closure to declare variable number of arguments. See the examples below.

//Two Explicit types arguments
def closure = {int a, int b ->
  a + b
}

//Two arguments
def closure = {a,b ->
  a + b
}

//Two arguments with one default
def closure = {int a, b = 5 ->
  a + b
}

//One Explicit type argument
def closure = {String name ->
  it.toUpperCase()
}

//One argument
def closure = {State ->
  it.toUpperCase()
}

//Varargs
def stringConcat = {String... it -> it.join('')}
stringConcat("abc","def","ghi")

def multiConcat = { int n, String... args ->                
    args.join('')*n
}
multiConcat(3,"John"," Carter ")

Using Closures

Some of the useful methods applicable to Closures are listed below. See here for more information.

//finds for the first match
def list = [1,2,30,34,42]
list.find{
	it > 30
}!=null

//finds all the matches
def list = [1,2,30,34,42]
def newList = list.findAll{
	it > 30
} == [34,42]

//Perform an operation on each
Map map = ['a':1, 'b':2] 
map.each{key, value -> 
  map[key] = value * 2  
}
map