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