Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 59 Next »

Informal Guide

When using the Java programming language most executable code is enclosed in either static class methods or instance methods. (Code can also be enclosed in constructors, initializers, and initialization expressions, but those aren't important here.) A method encloses code within curly brackets and assigns that block of code a method name. All such methods must be defined inside of a class of some type. For example, if you were to write a method that returned the square of any integer it may look like this:

Now in order to use the square() method you need to reference the class and the method by name as follows:

You can do the same thing in Groovy, but in groovy you can alternatively define the code without having to declare a class and a method as follows:

In Groovy, this anonymous code block is referred to as a closure definition (see the Formal Guide section below for a more elaborate definition of terms). A closure definition is one or more program statements enclosed in curly brackets. A key difference between a closure and method is that closures do not require a class or a method name.

As you can see, the executable code is the same except you didn't need to declare a class or assign the code a method name. While illustrative, the previous example is not all that useful because there is no way to use that closure once its created. It has no identifier (method name) so how can you call it? To fix that you assign the closure to a variable when it's created. You can than treat that variable as the identifier of the closure and make calls on it.

The following shows the square() method re-written as a closure:

What is really nice about closures is that you can create a closure, assign it to a variable, and then pass it around your program like any other variable. At first this seems a bit, well useless, but as you learn more about Groovy you'll discover that closures are used all over the place.

As an example, let's extend the java.util.Vector class from Java by adding a single method that allows you to apply a closure to every element in the vector. My new class, GVector, looks as follows:

The apply() method takes a closure as an input parameter. For each element in the GVector, the closure is called passing in the element. The resulting value is then used to replace the element. The idea is that you can modify the contents of the GVector in place using a closure which takes each element and converts into something else.

Now we can call our new apply() method with any closure we want. For example, we will create a new GVector, populate it with some elements, and pass in the closure we created earlier, the one that squares an integer value.

Because the apply() method on the GVector can be used with any closure, you can use any closure. For example, the following uses a closure that simply prints out the item its passed.

If you were to run the above script, assuming GVector from earlier is on your classpath, the output would look like this:

In addition to assigning closures to variables, you can also declare them directly as arguments to methods. For example, the above code could be re-written in the following manner:

This example accomplishes the same thing as the first, but the closure is defined directly as an argument to the apply method of GVector.

The other important difference of a closure to a normal method is that a closure can refer to variables from the scope in which it is called (in fact this is where this language construct gets its name). Here is an example:

In this example, the closure block { e -> e.salary > threshold } refers to the threshold variable defined in the highPaid() method. The example also used a closure to create the emps list.

Simulate Javascript-style variable arguments

Groovy supports varargs as Closure parameter, but that requires the use of a Object[] and the closure code has to access the varargs as an Array. 

In JavaScript, function arguments are fully dynamic and you could call a function with any number of arguments different from the number of arguments defined in a function, e.g.

If your closure defined more argument than closure call, then you could use:

However, you can't do the job exactly like a JavaScript function when your closure has defined less argument than the closure call. If you are the implementer of the closure, you could use varargs (as described in the Formal Guide) to allow your closure to take more arguments than it is defined, and you have to access the extra variables as an Object[].

There are cases you may want to take more arguments but not using varargs/Object[]. For example, as an API provider, you expose an API that take a closure as argument. The closure may define one or two parameters up to the user. (this is a typical case when passing JavaScript function) The following is an example about how to simulate such behaivior:

Other Examples

  • You could define a closure that take a closure as argument, and combine the use of other Groovy techniques to do a lot of things. See the Closure, Category and JPA example

Closures vs. Code Blocks

A closure looks a lot like a regular Java or Groovy code block, but actually it's not the same. The code within a regular code block (whether its a method block, static block, synchronized block, or just a block of code) is executed by the virtual machine as soon as it's encountered. With closures the statements within the curly brackets are not executed until the call() is made on the closure. In the previous example the closure is declared in line, but it's not executed at that time. It will only execute if the call() is explicitly made on the closure. This is an important differentiator between closures and code blocks. They may look the same, but they are not. Regular Java and Groovy blocks are executed the moment they are encountered; closures are only executed if the call() is invoked on the closure.

  • No labels