Preface:
So far, we have used the simple abbreviated syntax of closures: After a method call, put your code in curly braces with parameters delimited from the closure body by an arrow. Let’s start by adding to your knowledge about the simple abbreviated syntax, and then we’ll look at two more ways to declare a closure: by using them in assignments and by referring to a method.
The simple declaration:
Listing 5.1 shows the simple closure syntax plus a new convenience feature. When there is only one parameter passed into the closure, its declaration is optional. The magic variable it can be used instead. See the two equivalent closure declarations in listing 5.1.
Listing 5.1 Simple abbreviated closure declaration
Note that unlike counter , the magic variable it needs no declaration.
This syntax is an abbreviation because the closure object as declared by the curly braces is the last parameter of the method and would normally appear within the method’s parentheses. As you will see, it is equally valid to put it inside parentheses like any other parameter, although it is hardly ever used this way:
This syntax is simple because it uses only one parameter, the implicit parameter it . Multiple parameters can be declared in sequence, delimited by commas. A default value can optionally be assigned to parameters, in case no value is passed from the method to the closure. We will show examples in section 5.4.
Using assignments for declaration:
A second way of declaring a closure is to directly assign it to a variable:
The closure is declared inside the curly braces and assigned to the printer variable.
TIP. Whenever you see the curly braces of a closure, think: new Closure(){} .
There is also a special kind of assignment, to the return value of a method:
Again, the curly braces denote the construction of a new closure object. This object is returned from the method call.
TIP.
As you see, closures are objects. They can be stored in variables, they can be passed around, and, as you probably guessed, you can call methods on them. Being objects, closures can also be returned from a method.
Referring to methods as closures:
The third way of declaring a closure is to reuse something that is already declared: a method. Methods have a body, optionally return values, can take parameters, and can be called. The similarities with closures are obvious, so Groovy lets you reuse the code you already have in methods, but as a closure. Referencing a method as a closure is performed using the reference .& operator. The reference is used to specify which instance should be used when the closure is called, just like a normal method call to reference.someMethod() . Figure 5.1 shows an assignment using a method closure, breaking the statement up into its constituent parts.
Below demonstrates method closures in action, showing two different instances being used to give two different closures.
- Listing 5.2 Simple method closures in action
Method closures are limited to instance methods, but they do have another interesting feature—runtime overload resolution, also known as multimethods. You will find out more about multimethods in chapter 7, but listing 5.3 gives a taste.
- Listing 5.3 Multimethod closures—the same method name called with different parameters is used to call different implementations
Comparing the available options:
Listing 5.4 shows all of these ways of creating and using closures: through simple declaration, assignment to variables, and method closures. In each case, we call theeach method on a simple map, providing a closure that doubles a single value. By the time we’ve finished, we’ve doubled each value three times.
- Listing 5.4 Full closure declaration examples
In order to fully understand how closures work and how to use them within your code, you need to find out how to invoke them. That is the topic of the next section.
- Listing 7.5 Declaring methods
The main method has some interesting twists. First, the public modifier can be omitted because it is the default. Second, args usually has to be of type
String[] in order to make the main method the one to start the class execution. Thanks to Groovy’s method dispatch, it works anyway, although args is now implicitly of static typejava.lang.Object . Third, because return types are not used for the dispatch, we can further omit the void declaration.
NOTE.
So far, we have used the simple abbreviated syntax of closures: After a method call, put your code in curly braces with parameters delimited from the closure body by an arrow. Let’s start by adding to your knowledge about the simple abbreviated syntax, and then we’ll look at two more ways to declare a closure: by using them in assignments and by referring to a method.
The simple declaration:
Listing 5.1 shows the simple closure syntax plus a new convenience feature. When there is only one parameter passed into the closure, its declaration is optional. The magic variable it can be used instead. See the two equivalent closure declarations in listing 5.1.
Listing 5.1 Simple abbreviated closure declaration
- log = ''
- (1..10).each{ counter -> log += counter }
- assert log == '12345678910'
- log = ''
- (1..10).each{ log += it }
- assert log == '12345678910'
This syntax is an abbreviation because the closure object as declared by the curly braces is the last parameter of the method and would normally appear within the method’s parentheses. As you will see, it is equally valid to put it inside parentheses like any other parameter, although it is hardly ever used this way:
- log = ''
- (1..10).each({ log += it })
- assert log == '12345678910'
Using assignments for declaration:
A second way of declaring a closure is to directly assign it to a variable:
- def printer = { line -> println line }
TIP. Whenever you see the curly braces of a closure, think: new Closure(){} .
There is also a special kind of assignment, to the return value of a method:
- def Closure getPrinter() {
- return { line -> println line }
- }
TIP.
As you see, closures are objects. They can be stored in variables, they can be passed around, and, as you probably guessed, you can call methods on them. Being objects, closures can also be returned from a method.
Referring to methods as closures:
The third way of declaring a closure is to reuse something that is already declared: a method. Methods have a body, optionally return values, can take parameters, and can be called. The similarities with closures are obvious, so Groovy lets you reuse the code you already have in methods, but as a closure. Referencing a method as a closure is performed using the reference .& operator. The reference is used to specify which instance should be used when the closure is called, just like a normal method call to reference.someMethod() . Figure 5.1 shows an assignment using a method closure, breaking the statement up into its constituent parts.
Below demonstrates method closures in action, showing two different instances being used to give two different closures.
- Listing 5.2 Simple method closures in action
- class MethodClosureSample {
- int limit
- MethodClosureSample (int limit) {
- this.limit = limit
- }
- boolean validate (String value) {
- return value.length() <= limit
- }
- }
- // 1) Normal constructor calls
- MethodClosureSample first = new MethodClosureSample (6)
- MethodClosureSample second = new MethodClosureSample (5)
- // 2) Method closure assignment
- Closure firstClosure = first.&validate
- def words = ['long string', 'medium', 'short', 'tiny']
- assert 'medium' == words.find (firstClosure) // 3) Calling the closure
- assert 'short' == words.find (second.&validate) // 4) Passing a method closure directly
- Listing 5.3 Multimethod closures—the same method name called with different parameters is used to call different implementations
- class MultiMethodSample {
- int mysteryMethod (String value) {
- return value.length()
- }
- int mysteryMethod (List list) {
- return list.size()
- }
- int mysteryMethod (int x, int y) {
- return x+y
- }
- }
- MultiMethodSample instance = new MultiMethodSample()
- // 1) Only a single closure is created
- Closure multi = instance.&mysteryMethod
- // 2) Different implementations are called based on argument types
- assert 10 == multi ('string arg')
- assert 3 == multi (['list', 'of', 'values'])
- assert 14 == multi (6, 8)
Listing 5.4 shows all of these ways of creating and using closures: through simple declaration, assignment to variables, and method closures. In each case, we call theeach method on a simple map, providing a closure that doubles a single value. By the time we’ve finished, we’ve doubled each value three times.
- Listing 5.4 Full closure declaration examples
- map = ['a':1, 'b':2]
- map.each{ key, value -> map[key] = value * 2 } // 1) Parameter sequence with commas
- assert map == ['a':2, 'b':4]
- // 2) Assign and then call a closure reference
- doubler = {key, value -> map[key] = value * 2 }
- map.each(doubler)
- assert map == ['a':4, 'b':8]
- // 3) A usual method declaration
- def doubleMethod (entry){
- map[entry.key] = entry.value * 2
- }
- // 4) Reference and call a method as a closure
- doubler = this.&doubleMethod
- map.each(doubler)
- assert map == ['a':8, 'b':16]
- Listing 7.5 Declaring methods
- class TestClass {
- static void main(args) {
- def some = new TestClass()
- some.publicVoidMethod()
- assert 'hi' == some.publicUntypedMethod()
- assert 'ho' == some.publicTypedMethod()
- combinedMethod() // Call static method of current class
- }
- void publicVoidMethod(){}
- def publicUntypedMethod(){return 'hi'}
- String publicTypedMethod(){return 'ho'}
- protected static final void combinedMethod(){}
- }
NOTE.
This message was edited 25 times. Last update was at 17/01/2014 11:47:05