Skip to content

Closures

Philip Ford edited this page Aug 26, 2017 · 30 revisions

Summary

Groovy closures are true closures, with access to the variables in the context in which they are defined. They are:

  • Contained in braces

  • They are functions.

  • But they are also instances of the Closure class.

  • Used in callbacks

  • The default parameter (if you do not provide parameters) is it.

    [1,2,3].each {
       println it
    }

Closures vs. Lambdas

Syntactically, closures look a lot like lambdas, and they can be passed as parameters to another function. In other words, they are first-class objects. However, closures are instances of the Closure class, making them very different from Java 8 lambdas.

In addition, delegation is a key concept in Groovy closures which has no equivalent in lambdas. The ability to change the delegate or change the delegation strategy of closures make it possible to design beautiful domain specific languages (DSLs) in Groovy.

Syntax

{ <type> param1, <type> param2 ... ->
   //Body
}
  • Again, parameters are optional.
  • If no parameters are listed, the default parameter is it.
  • Parameters, if present, are listed (comma-separated) immediately after the first brace, on the first line of the closure, and followed by a arrow.
  • The code body begins on the next line.
  • The output of the last line is the return value, with or without the return statement.

Invoking Closures

Closures can be invoked like any other function:

closureName(<params...>)

Alternatively, closures can be invoked with the call method of the Closure class:

def c = { 123 }          // returns 123
assert c() == 123        // true
assert c.call()  == 123  // true

Closures as Parameters

For a function/closure to take a Closure as a parameter, that parameter must be declared as the Closure datatype.1

Example

private defaultResponseHandler = { resp, reader ->
   assert resp.status == 200
   println "My response handler got response: ${resp.statusLine}"
   println "Response length: ${resp.headers.'Content-Length'}"
   System.out << reader // print response reader
}

void fetchContent(String path, Closure c = defaultResponseHandler){
    connect path, TEXT, c
}

In this case, from my HttpClient, I make the Closure parameter optional by giving it a default value. In either case, the closure works as a callback.

Delegation

Owner, delegate, and this

To understand the concept of delegate, we must first explain the meaning of this inside a closure. A closure actually defines 3 distinct things:

  • this corresponds to the enclosing class where the closure is defined
  • owner corresponds to the enclosing object where the closure is defined, which may be either a class or a closure
  • delegate corresponds to a third party object where methods calls or properties are resolved whenever the receiver of the message is not defined

References

Notes

  1. Actually "must be declared" may be too strong. Perhaps def would work.

Clone this wiki locally