A closure in Groovy is an anonymous chunk of code that may take arguments, return a value, and reference and use variables declared in its surrounding scope. In many ways it resembles anonymous inner classes in Java, and closures are often used in Groovy in the same way that Java developers use anonymous inner classes. However, Groovy closures are much more powerful than anonymous inner classes, and far more convenient to specify and use.
Syntax for Defining a Closure
A closure definition follows this syntax:
Where closureArguments is an optional comma-delimited list of arguments, and statements are 0 or more Groovy statements. The arguments look similar to a method's parameter list, and these arguments may be typed or untyped. When a parameter list is specified, the | character is required and serves to seperate the arguments from the closure body. The statements portion consists of 0, 1, or many Groovy statements.
Some examples of valid closure definitions:
++++ Note: The examples could definitely be made more real-life MWS
Closures appear to be a convenient mechanism for defining something like an inner classs, but the semantics are in fact more powerful and subtle than what an inner class offers. In particular, the properties of closures can be summarized in this manner:
- They are always anonymous
- They have one implicit method (which is never specified in a closure definition) called call()
- Closures may have 1...N arguments, which may be statically typed or untyped. The first parameter is always available via an implicit untyped argument named it. If the caller does not specify any arguments, the first parameter (and, by extension, it) will be null.
- The developer does not have to use it for the first parameter. If they wish to use a different name, they may specify it in the parameter list.
- Closures always return a value. This may occur via either an explicit return statement, or as the value of the last statement in the closure body (e.g. an explicit return statement is optional).
- A closure may reference any variables defined within its enclosing lexical scope. Any such variable is said to be bound dynamically to the closure
- Any variables bound to a closure are available to the closure even when the closure is returned outside of the enclosing scope.
- Closures are first class objects in Groovy, and are always of type Closure. Code which uses closures may reference them via untyped variables or variables typed as Closure.
- The body of a closure is not executed until it is explicitly invoked e.g. a closure is not invoked at its definition time
- A closure may be invoked via the call() method, or with a special syntax of an unnamed () invocation
These properties are explained further in the following sections.
Closures are anonymous
Closures in Groovy are always represented as anonymous blocks. Unlike a Java or Groovy class, you cannot have a named closure. You may however reference closures using untyped variables or variables of type Closure, and pass such references as method arguments and arguments to other closures.
Closures are considered to have one implicitly defined method, which corresponds to the closure's arguments and body. You cannot override or redefine this method. This method is always invoked by the call() method on the closure, or via the special unnamed () syntax.
A closure always has at least one argument, which is available within the body of the closure via the implicit parameter it. The developer never has to declare the it variable - like the this parameter within objects, it is always implicitly available.
If a closure is invoked with zero arguments, then it will be null.
Explicit closure arguments may be specified by the developer as defined in the syntax section. These arguments are a list of 1 or more argument names which are comma seperated. The parameter list is terminated with a | character. Each of these arguments may be specified "naked" e.g. without a type, or with an explicit static type.
The first parameter in an explicit parameter list is an alias for the it variable.
For arguments that have a declared type, this type will be checked at runtime. If a closure invocation has 1 or more arguments which do not match the declared argument type(s), then an exception will be thrown at runtime. Note that this argument type checking always occurs at runtime; there is no static type checking involved, so the compiler will not warn you about mis-matched types.
Groovy has special support for excess arguments. A closure may be declared with its last argument of type Object. If the developer does this, any excess arguments at invocation time are placed in this array. This can be used as a form of support for variable numbers of arguments. For example:
Both invocations of c are valid. In the first case the closure will receive the parameter args with 3 elements ("one", "two", "three"), in the second case the closure will receive the parameter args with 1 element ("1");
++++ What Exception is thrown? MWS
Closure Return Value
Closures always have a return value. The value may be specified via one or more explicit return statement in the closure body, or as the value of the last executed statement if return is not explicitly specified. If the last executed statement has no value (for example, if the last statement is a call to a void method), then null is returned.
There is currently no mechanism for statically declaring the return type of a closure.
References to External Variables
Closures may reference variables external to their own definition. This includes local variables, method parameters, and object instance members. However, a closure may only reference those variables that the compiler can lexically deduce from the physical location of the closure definition within the source file.
Some examples might serve to clarify this. The following example is valid and shows a closure using a method's local variables and a method parameter:
The above code will print out:
Looking at the definition of class A, the closure inside of publicMethod has access to all variables that publicMethod may legally access. This is true whether the variables are local variables, parameters, instance members, or method invocations.
When a closure references variables in this way, they are bound to the closure. At the same time, the variables are still available normally to the enclosing scope, so the closure may read/change any such values, and external scopes may read/change the same variables.
If such a closure is returned from the enclosing scope, the variables bound with the closure also live on. This binding actually occurs when the closure is instantiated. If an object method or instance member is referenced, then a reference to that object is stored within the closure and that referenece is used when the member or method is referenced. If a local variable or parameter is referenced, then the compiler re-writes the local variable or parameter reference so that the reference is taken off the stack and stored in an heap based object that is then referenced within the method body and closure.
++++ This section needs re-wording, I think MWS
It's important to keep in mind that these references only are ever allowed according to the lexical structure available to the compiler (in this case, the A class). This process does not occur dynamically by looking at the call stack. So the following will not work:
The above code is similar to the first example, except that we now have a class B which dynamically instantiates an object of type A and then calls A.publicMethod(). However, in this code the closure within publicMethod() is trying to reference a member from B, and this is not allowed since the compiler cannot statically determine that this is available. Some older languages allowed this sort of reference to work, by dynamically examining the call stack at runtime, but this is disallowed in Groovy.
The Closure Type
All closures defined in Groovy are derived from the type Closure. Each unique closure definition with a Groovy program creates a new unique class which extends Closure. If you wish the specify the type of a closure in a parameter, local variable, or object member instance, then you should use the Closure type.
Closure creation and invocation
Closures are created implicitly when their surrounding scope encounters them. For example, in the following code two closures are created:
In the above example, closureVar holds a reference to a different closure object than closureVar2. Closures are always implicitly created in this manner - you cannot new a closure programmatically.
++++ Is this true? Could you do something funky with new'ing a Closure() object and programmatically build one up? MWS
Closures may be invoked using one of two mechanisms. The explict mechanism is to use the call() method:
You may also use the implict nameless invocation approach:
If you are looking at the Closure javadoc, you may notice that the call method within the Closure class is defined as:
Despite this method signature, you do not have to manually write code to turn parameters into the Object array. Instead, invocations use the normal method argument syntax, and Groovy converts such calls to use an object array:
Both calls above are legal Groovy. However, if you are dealing with a Closure from Java code you will need to create the Object array yourself
Special Case: Passing Closures to Methods
Groovy has a special case for defining closures as method arguments to make the closure syntax easier to read. Specifically, if the last argument of a method is of type Closure, you may invoke the method with an explicit closure block outside of the parenthesis. For example, if a class has a method:
Then you may invoke each() with a closure definition outside of the parenthesis:
The more traditional syntax is also available, and also note that in Groovy you can elide parenthesis in many situations, so these two variations are also legal:
The same rule applies even if the method has other arguments. The only restriction is that the Closure argument must be last:
This syntax is only allowed when explicitly defining a closure within the method call. You cannot do this with a variable of type closure:
Comparing Closures to Anonymous Inner Classes
Groovy includes closures because they allow the developer to write more concise and more easily understood code. Where Java developers may use single-method interfaces (Runnable, the Command pattern) combined with anonymous inner classes, Groovy allows you to accomplish the same sort of tasks in a less verbose manner. In addition, closures have fewer constraints than anonymous inner classes and include extra functionality.
Most closures are relatively short, isolated, and anonymous snippets of code that accomplish one specific job. Their syntax is streamlined to make closure definitions very short and easy to read without additional clutter. For example, in Java code you might see code like this for an imaginary GUI system:
The same code in Groovy would look like this:
The Groovy code accomplishes the same task but is much clearer and without extra syntactical clutter. This is the first
rule of Groovy closures - closures are trivially easy to write. In addition, closures may reference any variables in its outer
defining scope without the restrictions of anonymous inner classes - in particular, such variables do not need to be final.
Closures also carry their state around with them, even when they reference local variables and parameters. Closures may also take advantage of Groovy's optional dynamic typing so that you don't have to statically declare all of your closure arguments or return types (in fact, a Groovy closure can take varying numbers of parameters from invocation to invocation).
What Groovy closures lack compared to an approach using Command-like interfaces is the level of static typing involved. A Java interface rigidly enforces what type of objects can be used and the method(s) that may be called in it. In Groovy, all closures type equally as Closure and type checking of arguments (if specified in the closure definition) is deferred until Runtime.
You can see more documentation on closures here