Groovy from the ground up

Closures

Blocks of code you can name and pass around, the implicit `it`, and the three methods that replace most loops: collect, findAll and find.

Part 7 of 12

Intermediate18 min readBack to Courses

A block of code with a value

A closure is written between braces: { it * 2 }. Everything before the arrow names its parameters, so { a, b -> a + b } takes two and { it * 2 } takes one that is already called it. The last expression is the answer, just like a method.

The point of a closure is that it is a value: it can be stored in a variable, passed to a method, and returned. def twice = { it * 2 } makes one, twice(5) calls it, and numbers.collect(twice) hands it to a list — code travelling as data. A closure is compiled to a class of its own, which is why it can outlive the line that made it and be passed to a method that has never heard of your script.

The three methods that replace a loop

collect runs the block once per item and gathers the answers into a new list: numbers.collect { it * it }. findAll keeps the items where the block is true: numbers.findAll { it > 3 }. find answers the first item that matches, or null if none does.

That is the shape of most list work, and saying it this way has a practical advantage over a loop with a growing list: the intent is the first word on the line. The original list is never changed, so a typo cannot leave you with half-transformed data. find answers null when nothing matches and findAll answers an empty list; neither throws, so the check for "nothing found" belongs in your own code.

Closures remember what they can see

A closure can use the variables around it, and it keeps them — change factor after building scale and the closure sees the new value. That is what "closure" means: the code carries its surroundings with it. It keeps a reference rather than a copy, so a closure is not a snapshot: if you need the value as it was, copy it into a new variable before you build the closure.

each is the one to use when you want a side effect such as printing, and it answers the list it walked. every and any answer a single true or false. All of these take the same kind of block, which is why learning one teaches you the others.

Naming, passing and using a block

def numbers = [1, 2, 3, 4, 5]

println numbers.collect { it * 10 }
println numbers.findAll { it % 2 == 1 }
println numbers.find { it > 3 }
println numbers.every { it > 0 }
println numbers.any { it > 4 }

// A closure can carry a value from around it.
def factor = 3
def scale = { it * factor }
println scale(5)

def sentence = { words -> words.join(' ') }
println sentence(['Groovy', 'reads', 'well'])

A closure is an object with a call

{ it * 2 } creates an instance of Closure. Calling twice(5) is shorthand for twice.call(5), and passing it to collect hands the object over; the method decides when to call it. That is why a closure can be stored, returned and reused — it is a value like any other.

The parameter list before the arrow is the closure's signature. With no arrow, a one-parameter closure is assumed and the parameter is named it. With two parameters you must name them: { a, b -> a + b }. A closure with no parameters writes the arrow with nothing before it: { -> println 'once' }.

collect, each and inject

collect answers a new list with one result per item. each answers the original collection and exists for side effects. inject carries an accumulator through the list: [1, 2, 3].inject(0) { sum, n -> sum + n } answers 6, and the first argument is the starting value.

Those three cover most list work. findAll filters, find picks the first match, every and any reduce to a boolean. The names are worth learning as a set because they compose: a findAll followed by a collect is a filter and a map, and the original list is untouched by either.

Three closure mistakes: each answers the original list, not the block's results, so use collect when you want the transformed values; nested closures both use it, and the inner one hides the outer, so name the parameters; and a return inside a closure leaves the closure only, not the method that called it.

Try these next

  • Turn a list of numbers into their squares with collect, then keep the even squares.
  • Add the prices of a list of maps with inject and a starting value of 0.
  • Name the parameter in a closure that uses it, and check the output is unchanged.

Four numbers, five answers

Run it, then add a `collect` that turns each number into a short label — for example `3!` — using interpolation, and a `findAll` that keeps only the numbers above 5.

Output
Click "Run" to execute your code

A basket, reduced

Run it and check the total against the three prices. Then add a fourth item, and add a `findAll` that keeps only items cheaper than 5.

Output
Click "Run" to execute your code

A one-parameter closure gets it for free. Naming it — { number -> number * 2 } — costs four words and buys a reader who does not have to remember what the block is about.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What is it inside { it * 2 }?