A closure is a block of code you can store in a variable, pass to a method and run later. Closures are to Groovy what lambdas are to Java — but more flexible, and used everywhere: every each, collect and findAll takes one, and so do Gradle and Jenkins DSLs.
Writing a closure
def greet = { String name -> "Hello, $name!" }
greet('Acme') // Hello, Acme!
greet.call('Globex') // the same, explicitly
Parameters go before ->, the body after. The last expression is returned.
The implicit it
A closure with one parameter can skip declaring it — it’s called it:
def shout = { it.toUpperCase() + '!' }
shout('boomi') // BOOMI!
No parameters, many parameters, defaults
def now = { -> 'it is time' } // explicitly zero parameters
def lineTotal = { qty, price, discount = 0 ->
qty * price * (1 - discount / 100)
}
lineTotal(10, 20) // 200
lineTotal(10, 20, 15) // 170.00
Passing closures to methods
def retry(int times, Closure action) {
for (attempt in 1..times) {
try {
return action(attempt)
} catch (e) {
println "attempt $attempt failed: ${e.message}"
}
}
'gave up'
}
When the last argument is a closure, you can write it after the parentheses — this “trailing closure” syntax is what makes Groovy DSLs read like plain language:
def result = retry(3) { attempt ->
if (attempt < 3) throw new RuntimeException('timeout')
"succeeded on attempt $attempt"
}
Closures capture variables — and can change them
def total = 0
[250, 90, 1200].each { amount -> total += amount }
println total // 1540
The closure sees total from the surrounding code and updates it. (Java lambdas can’t do this — they require effectively final variables. Groovy closures hold a reference to the variable itself.)
Returning closures: function factories
def multiplier = { factor -> { number -> number * factor } }
def triple = multiplier(3)
triple(7) // 21
Each returned closure remembers its own factor — the essence of a closure.
Method pointers
The .& operator turns a method into a closure:
def isValid = this.&isValidSku // a method in the script
['AX-200', 'bad'].findAll(isValid)
def upper = 'x'.&toUpperCase
The full example
// closures.groovy — closures: blocks of code you can store, pass and call.
// A closure literal: { parameters -> body }
def greet = { String name -> "Hello, $name!" }
println greet('Acme') // call like a method
println greet.call('Globex') // or with call()
// One parameter? Use the implicit 'it'
def shout = { it.toUpperCase() + '!' }
println shout('boomi')
// No parameters
def now = { -> 'it is time' }
println now()
// Multiple parameters and default values
def lineTotal = { qty, price, discount = 0 -> qty * price * (1 - discount / 100) }
println lineTotal(10, 20) // 200
println lineTotal(10, 20, 15) // 170.00
// Closures as arguments: the last closure can go outside the parentheses
def retry(int times, Closure action) {
for (attempt in 1..times) {
try {
return action(attempt)
} catch (e) {
println " attempt $attempt failed: ${e.message}"
}
}
'gave up'
}
def result = retry(3) { attempt ->
if (attempt < 3) throw new RuntimeException('timeout')
"succeeded on attempt $attempt"
}
println result
// Closures capture variables from where they were defined — and CAN change them
def total = 0
[250, 90, 1200].each { amount -> total += amount }
println "Total: $total" // 1540
// Returning closures: a function factory
def multiplier = { factor -> { number -> number * factor } }
def triple = multiplier(3)
println triple(7) // 21
// Method pointer: turn a method into a closure with .&
def upper = 'x'.&toUpperCase // bound to the string 'x'
println upper() // X
def isEven = { it % 2 == 0 }
println([1, 2, 3, 4].findAll(isEven)) // [2, 4]
Try it yourself
Write a closure timeIt that takes another closure, runs it, prints how many milliseconds it took, and returns its result.
Show a solution
def timeIt = { Closure work ->
long start = System.currentTimeMillis()
def result = work()
println "took ${System.currentTimeMillis() - start} ms"
result
}
def sum = timeIt { (1L..1_000_000L).sum() } // longs: the total is too big for an int
println sum // 500000500000
Key takeaways
{ params -> body }creates a closure; one parameter can be the implicitit.- Pass closures to methods; a last-argument closure can go outside the parentheses.
- Closures capture surrounding variables and can modify them.
- Closures can return closures, and
.&turns methods into closures.