Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

A closure definition follows this syntax:

Code Block
java
java

{ [closureArguments->] statements }

...

Some examples of valid closure definitions:

Code Block
java
java

{ item++ }

{ println it }

{ ++it }

{ name -> println name }

{ String x, int y -> println "hey ${x} the value is ${y}" }

{ reader ->
  while (true) {
      def line = reader.readLine()
  }
}

...

  1. They have one implicit method (which is never specified in a closure definition) called doCall()
  2. 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.
  3. 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.
  4. 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.
  5. 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).
  6. A closure may reference any variables defined within its enclosing lexical scope. Any such variable is said to be bound to the closure.
  7. Any variables bound to a closure are available to the closure even when the closure is returned outside of the enclosing scope.
  8. 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.
  9. The body of a closure is not executed until it is explicitly invoked, e.g. a closure is not invoked at its definition time.
  10. A closure may be curried so that one a copy of 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.

...

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:

Code Block
java
java

def c = {
     format, Object[] args ->
     aPrintfLikeMethod (format, args)}
c ("one", "two", "three");
c ("1");

...

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:

Code Block
java
java

public class A {
    private int member = 20;

    private String method()
    {
      return "hello";
    }

    def publicMethod (String name_)
    {
      def localVar = member + 5;
      def localVar2 = "Parameter: ${name_}";
      return {
        println "${member} ${name_} ${localVar} ${localVar2} ${method()}"
      }
    }
}

A sample = new A();
def closureVar = sample.publicMethod("Xavier");
closureVar();

The above code will print out:

Code Block
java
java

20 Xavier 25 Parameter: Xavier hello

...

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:

Code Block
java
java

class A {
    private int member = 20;

    private String method()
    {
      return "hello";
    }

    def publicMethod (String name_)
    {
      def localVar = member + 5;
      def localVar2 = "Parameter: name_";
      return {
        // Fails!
        println "${member} ${name_} ${localVar} ${localVar2} ${method()} ${bMember}"
      }
    }
  }

  class B {
    private int bMember = 12;

    def bMethod (String name_)
    {
      A aInsideB = new A();
      return (aInsideB.publicMethod (name_));
    }

  }

  B aB = new B();
  closureVar = aB.bMethod("Xavier");
  closureVar();

...

Groovy supports the special owner variable which can be used when a closure argument is hiding an object member variable. For example:

Code Block
java
java

class HiddenMember {
  private String name;

  def getClosure (String name)
  {
    return { name -> println (name)}
  }
}

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:

Code Block
java
java

class HiddenMember {
  private String name;

  def getClosure (String name)
  {
    return { name -> println ("Argument: ${name}, Object: ${owner.name}")}
  }
}

...

The exact type of a closure is not defined unless you are explicitly subclasses the Closure class. Using this example:

Code Block
java
java

def c = { println it}

The exact type of the closure referenced by c is not defined, we know only that it is a subclass of Closure.

...

Closures are created implicitly when their surrounding scope encounters them. For example, in the following code two closures are created:

Code Block
java
java

class A {
    private int member = 20;

    private method()
    {
      println ("hello");
    }

    def publicMethod (String name_)
    {
      def localVar = member + 5
      def localVar2 = "Parameter: name_";
      return {
        println "${member} ${name_} ${localVar} ${localVar2} ${method()}"
      }
    }
  }

  A anA = new A();
  closureVar = anA.publicMethod("Xavier");
  closureVar();
  closureVar2 = anA.publicMethod("Xavier");
  closureVar2();

...

Closures may be invoked using one of two mechanisms. The explict mechanism is to use the call() method:

Code Block
java
java

closureVar.call();

You may also use the implict nameless invocation approach:

Code Block
java
java

closureVar();

If you are looking at the Closure javadoc, you may notice that the call method within the Closure class is defined as:

Code Block
java
java

public Object call (Object[] args);

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:

Code Block
java
java

closure ("one", "two", "three")
  closure.call ("one", "two", "three")

...

A simple example of this would be:

Code Block
java
java

def c = { arg1, arg2-> println "${arg1} ${arg2}" }
def d = c.curry("foo")
d("bar")

...

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:

Code Block
java
java

class SomeCollection {
    public void each (Closure c)
  }

Then you may invoke each() with a closure definition outside of the parenthesis:

Code Block
java
java

SomeCollection stuff = new SomeCollection();
  stuff.each() { println it }

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:

Code Block
java
java

SomeCollection stuff = new SomeCollection();
  stuff.each { println it }     // Look ma, no parens
  stuff.each ({ println it })   // Strictly traditional

The same rule applies even if the method has other arguments. The only restriction is that the Closure argument must be last:

Code Block
java
java

class SomeCollection {
    public void inject (x, Closure c)
  }

  stuff.inject(0) {count, item -> count + item  }    // Groovy
  stuff.inject(0, {count, item -> count + item  })    // Traditional

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:

Code Block
java
java

class SomeCollection {
    public void inject (x, Closure c)
  }

  counter = {count, item -> count + item  }
  stuff.inject(0) counter               // Illegal!  No Groovy for you!

When you are not defining a closure inline to a method call, you cannot use this syntax and must use the more verbose syntax:

Code Block
java
java

class SomeCollection {
    public void inject (x, Closure c)
  }

  def counter = {count, item -> count + item  }
  stuff.inject(0,counter)

...

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:

Code Block
java
java

Button b = new Button ("Push Me");
  b.onClick (new Action() {
    public void execute (Object target)
    {
      buttonClicked();
    }
  });

The same code in Groovy would look like this:

Code Block
java
java

Button b = new Button ("Push Me");
  b.onClick { buttonClicked() }

...

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:

Code Block
java
java

f = { println "f called" }
m = [ (f): 123 ]

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.

Code Block
java
java

println m.get(f)    // 123
println m[f]        // 123
println m.f         // null

...

You can use a closure as a value and call that closure as if it were a method on the map, similarly to Expandos.

Code Block
java
java

m = [ f: { println 'f called' } ]
m.f()    // f called

m = new Expando( f: { println 'f called' } )
m.f()      // f called

...

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.

Code Block
java
java

dir = new File("/tmp")
use(ClassWithEachDirMethod.class) {
  dir.eachDir {
    println it
  }
}

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.

Code Block
java
java

public class ClassWithEachDirMethod {
  public static void eachDir(File self, Closure closure) {
      File[] files = self.listFiles();
      for (int i = 0; i < files.length; i++) {
          if (files[i].isDirectory()) {
              closure.call(files[i]);
          }
      }
  }
}

...