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.
In functional language parlance, such an anonymous code block might be referred to as an anonymous lambda expression in general or lambda expression with unbound variables or a closed lambda expression if it didn't contain references to unbound variables (like
threshold in the earlier example). Groovy makes no such distinction.
Strictly speaking, a closure can't be defined. You can define a block of code that refers to local variables or fields/properties, but it becomes a closure only when you "bind" (give it a meaning) this block of code to variables. The closure is a semantic concept, like an instance, which you cannot define, just create. Strictly spoken a closure is only a closure if all free variables are bound. Unless this happens it is only partially closed, hence not really a closure. Since Groovy doesn't provide a way to define a closed lambda function and a block of code might not be a closed lambda function at all (because it has free variables), we refer to both as closure - even as syntactic concept. We are talking about it as syntactic concept, because the code of defining and creating an instance is one, there is no difference. We very well know that this terminology is more or less wrong, but it simplifies many things when talking about code in a language that doesn't "know" the difference.
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 have one implicit method (which is never specified in a closure definition) called doCall()
- A closure may be invoked via the call() method, or with a special syntax of an unnamed () invocation. Either invocation will be translated by Groovy into a call to the Closure's doCall() method.
- Closures may have 1...N arguments, which may be statically typed or untyped. The first parameter is available via an implicit untyped argument named it if no explicit arguments are named. 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 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 derived from the class 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 curried so that one a copy the closure is made with one or more of its parameters fixed to a constant value
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. The implicit method name is doCall().
A closure always has at least one argument, which will be available within the body of the closure via the implicit parameter it if no explicit parameters are defined. The developer never has to declare the it variable - like the this parameter within objects, it is 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. If an explicit parameter list is specified, then the it variable is not available.
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. Since the closure defines two arguments (format and args) and the last argument is of type Object, the first parameter in any call to c will be bound to the format argument and the remaining parameters will be bound to the args argument. In the first call of c the closure will receive the parameter args with 2 elements ("two", "three") while the format parameter will contain the string "one". In the second call the closure will receive the parameter args with no elements and the format parameter will contain the string "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 returnis 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 publicMethodhas 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 code from the outer scope may read/change the same variables.
If such a closure is returned from its enclosing scope, the variables bound with the closure also live on. This binding occurs when the closure is instantiated. If an object method or instance member is used within a closure, then a reference to that object is stored within the closure. If a local variable or parameter is referenced, then the compiler re-writes the local variable or parameter reference so that the local variable or parameter is taken off the stack and stored in an heap based object.
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 Aand 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.
Groovy supports the special owner variable which can be used when a closure argument is hiding an object member variable. For example:
In the above code the println (name) call is referencing the parameter name. If the closure needs to access the name instance variable of class HiddenMember, it can use the owner variable to indicate this:
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.
The exact type of a closure is not defined unless you are explicitly subclasses the Closure class. Using this example:
The exact type of the closure referenced by c is not defined, we know only that it is a subclass of Closure.
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.
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
Fixing Closure Arguments to Constant Values Via Currying
You can fix the values for one or more arguments to a closure instance using the curry() method from the Closuretype. In fact, this action is often referred to as currying in functional programming circles, and the result is generally referred to as a Curried Closure. Curried closures are very useful for creating generic closure definitions, and then creating several curried versions of the original with differing parameters bound to them.
When the curry() method is called on a closure instance with one or more arguments, a copy of the closure is first made. The incoming arguments are then bound permanently to the new closure instance so that the parameters 1..N to the curry() call are bound to the 1..N parameters of the closure. The new curried closure is then returned the caller.
Callers to the new instance will have their invocation parameters bound to the new closure in the N+1 parameter position of the original closure.
A simple example of this would be:
The above code defines a closure c, and then calls c.curry("foo"). This returns a curried closure with the arg1 value permanently bound to the value "foo". On the invocation d("bar"), the "bar" parameter comes into the closure in the arg2 argument. The resulting output would be foo bar.
See also: Functional Programming with Groovy
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, as this example shows:
When you are not defining a closure inline to a method call, you cannot use this syntax and must use the more verbose syntax:
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.
Closures as map keys and values
It's possible to put closures in a map, both as keys and values.
Closures as keys
You can use a closure as a key. However, when putting it into the map you must "escape" it (as you would any other identifier you don't want treated as a string) by enclosing it in parens, like so:
When accessing the value of the closure in the map you must use get(f) or m[f] as m.f will treat f as a string.
Closures as values
You can use a closure as a value and call that closure as if it were a method on the map, similarly to Expandos.
Extending groovy with the use directive
You can provide your own specialized methods supporting closures by implementing a Java class containing such methods. These methods must be static and contain at least two parameters. The first parameter to the method must be the type on which the method should operate, and the last parameter must be a Closure type.
Consider the example below, which is a variant of the eachFile method which simply ignores files, and just prints the directories within the dir object on which the method operates.
Take note of the use() directive. This will tell groovy where the eachDir method is implemented. Below is the Java code required to support the eachDir method on the File object, as shown.
To support additional parameters, these should be placed between the first and last.