From Java to Groovy, part 2: closures and native syntax for lists
In our previous installment, we've discovered how Java and Groovy's syntaxes are so close that you can even cut and paste valid Java code in your Groovy scripts. Along the way, we've also learned how to make your usual Java code much groovyier. Today, we'll continue this series, by learning about Groovy's native syntax support for lists, as well as discovering closures and their usefulness. To proceed, I'll steal the example class from Paul King's Groovy presentation, and we'll see how we can transform this Java class in Groovy.
The following Java class is also a valid Groovy class. Its purpose is to filter a list of names to remove the names which are longer than three characters. We'll create a list of names, we will call a utility method that is responsible of the filtering, and we'll print the result.
import java.util.*;
public class Erase {
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted");
names.add("Fred");
names.add("Jed");
names.add("Ned");
System.out.println(names);
Erase e = new Erase();
List shortNames = e.filterLongerThan(names, 3);
System.out.println (shortNames.size());
for (Iterator i = shortNames.iterator(); i.hasNext(); ) {
String s = (String) i.next();
System.out.println(s);
}
}
public List filterLongerThan (List strings, int length) {
List result = new ArrayList();
for (Iterator i = strings.iterator(); i.hasNext(); ) {
String s = (String) i.next();
if (s.length() < length+1) {
result.add(s);
}
}
return result;
}
}
We could certainly improve this Java example by using the Arrays#asList() method to save some lines. Anyway, we are going to follow the same path as in the previous article one more time to "groovyfy" this program. First of all, if you recall, we can get rid of semi-colons, but at the same time, to go a bit further, we are going to:
- use a nicer syntax for the for loop, more in line with the Java 5 for loop,
- we will also remove the imports, as Groovy imports java.util by default,
- the implicit default public modifier for methods and classes
- and we will use the println command
class Erase {
static void main(String[] args) {
List names = new ArrayList()
names.add("Ted")
names.add("Fred")
names.add("Jed")
names.add("Ned")
println names
Erase e = new Erase()
List shortNames = e.filterLongerThan(names, 3)
println shortNames.size()
for (String s in short_names) {
println s
}
}
List filterLongerThan (List strings, int length) {
List result = new ArrayList()
for (String s in strings) {
if (s.length() < length+1) {
result.add(s)
}
}
return result
}
}
Instead of using a class with a method main(), we're going to transform this code into a script, and we'll abandon the static typing information as well:
def names = new ArrayList()
names.add("Ted")
names.add("Fred")
names.add("Jed")
names.add("Ned")
println names
def shortNames = filterLongerThan(names, 3)
println shortNames.size()
for (s in shortNames) {
println s
}
def filterLongerThan (strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.length() < length+1) {
result.add(s)
}
}
return result
}
Instead of creating an instance of the Erase class, we just call the filterLongerThan() method. So far, these little transformations are not really new, since we had already followed these steps previously. What we are going to discover now is how to make the lists a little more friendly, thanks to Groovy's native syntax for lists. So how can we define a new list?
def names = []
And instead of adding an element one at a time to our list, we can fill it directly:
def names = ["Ted", "Fred", "Jed", "Ned"]
You can set and access elements with the subscript operator:
assert names[1] == "Fred"
names[1] = "Frederic"
Groovy also adds some useful methods on lists to simplify list activities such as enumerating the elements. Groovy does so by "decorating" the core JDK classes. Two handy methods added on lists are the each() method for iterating over all the elements, and the findAll() method to find all the elements matching some condition. At the same time, we will discover closures! Basically, without trying to provide a proper theoretical definition of closures, let's just think of closures as blocks of code, of statements, that can access all the variables or methods of the surrounding scope, and you can assign these blocks of code to variables, and pass them around elsewhere. Steven gives some more in-depth examples of closures in his post about higher-order functions. So now, I'm sure you want to see what a closure looks like?
def c = { println "hello" }
c()
See, that's simple, a closure is just a statement or a list of statements, delimited with curly braces. And you can assign it to a variable, and call this closure afterwards like a normal method call. A closure has a default implicit parameter called 'it'. But you can also provide a list of parameters, either typed or not typed. Also, like this is the case in methods, the last expression of a closure is the return value of the closure. But you can also use the return keywords, if you feel it is more readable.
def square = { it * it }
assert square(3) == 9
def isStringLongerThan = { String s, int i -> return s.size() > i }
assert isStringLongerThan("Guillaume", 4) == true
assert isStringLongerThan("Fred", 6) == false
// a more concise version could be:
// def isStringLongerThan = { s, i -> s.size() > i }
Now that we've discovered what closures look like and how you can assign them, and call them, we are going to see how we can pass a closure as a parameter to another method, as this is what we're going to do with the each() and findAll() methods that Groovy adds on collections.
def logCall(Closure c) {
println "Calling closure"
def start = System.currentTimeMillis()
println "Result: " + c()
def end = System.currentTimeMillis()
println "End of call (duration: ${end - start} ms)"
}
logCall({ return "Groovy is cool!" })
// but you can also remove the parentheses:
logCall { return "Groovy is cool!" }
We are now almost experts in closures, so let's put our knowledge into action by using the each() method on collections, to be able to call a closure on each element of the list. We are going to print all the names of the list:
shortNames.each({ String name -> println name })
// now without the parentheses
shortNames.each { String name -> println name }
// and with the implicit 'it' parameter
shortNames.each { println it }
After using each() to apply a closure on each element, we'll create a new list by filtering the original list of names according to a filter closure, thanks to the findAll() method. This method will find all the elements of a collection which match the criteria represented by the closure passed as parameter. This closure will be called on each element and the findAll() method will simply return a boolean value saying whether the current value matches or not. And in the end, a new list containing the elements that match will be returned. This closure will replace our filterLongerThan() method of our original Java class.
def shortNames = names.findAll { it.size() <= 3 }
Groovy provides several other methods of that kind, such as:
- find(): find the first element that matches
- every(): returns true if all the elements matches the criteria closure
- any(): returns true if at least one element matches
def names = ["Ted", "Fred", "Jed", "Ned"]
println names
def shortNames = names.findAll { it.size() <= 3 }
println shortNames.size()
shortNames.each { println it }
For simple tasks like this list filtering, you can leverage Groovy's closures and native syntax for lists. At this point, you know how to write concise Groovy scripts by using its sensible defaults, its native syntax constructs, and by applying closures and collection facilities. In the next installements, we'll certainly stop the long process of transforming Java classes into Groovy scripts, but we'll learn about other Groovy syntax tricks for handling maps, ranges and regular expressions.
- Login or register to post comments
- 7361 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
ff aaa replied on Sun, 2008/02/03 - 7:27pm
Rick Ross replied on Sun, 2008/02/03 - 7:43pm
in response to: afsina
[quote=afsina]Due all respect, please stop writing java code exaggeratedly verbose. Telling "We could certainly improve this Java example by using the Arrays#asList() method..." or not using enhanced for loops deliberately in two places, putting unnecessary declaration are not the correct way of making a case that groovy syntax is not verbose. Developers are smart, let them decide by themselves. Put your efforts to make groovy not 100 times slower then java compiled code first please.[/quote]
In the spirit of positive dialogue, I think it would be good in a reply like this to actually include a rewrite of the code in question. A respectful exchange between several programmers, focused solidly on the code, is sure to have helpful results. I bet I'm not the only one who would like to see the non-verbose Java code you're thinking of, so we can compare it against the Groovy code for the discussion. That would be pretty neat.
Thanks,
Rick
Rick Ross replied on Sun, 2008/02/03 - 9:13pm
in response to: afsina
Andres Almiray replied on Sun, 2008/02/03 - 9:57pm
in response to: afsina
Unfortunately Ahmet, you left some lines out from your example: no imports, no class definition. I know developers are not stupid, but what about "programmers" ? a developer is a person that takes pride in his/her craft so he/she is always looking for info and learning new things; in contrast a programmer is just doing what he/she is told (no thought process involved in most of the cases, sad but true). These are the persons that flood forums and chat sessions with "bad/verbose" code as the one you see at the beginning of the article. This article is more oriented to programmers, people that do the heavy lifting but developers can also benefit from reading them.
As you pointed out, using a helper library may reduce the code, so here is my take using commons-collection, which I think could also be improve.
ff aaa replied on Sun, 2008/02/03 - 11:42pm
in response to: afsina
ff aaa replied on Mon, 2008/02/04 - 12:01am
in response to: aalmiray
vaclav replied on Mon, 2008/02/04 - 1:43am
I'm curious about the step when static typing was abandoned. What benefits do we get from that? Shouldn't we keep type info and benefit from type-checking, type-aware IDE code-completion and code inspections?
You see, I'm a bit petrified in the Java coding style and have hard time justifying removing type info for no aparent reason.
Guillaume Laforge replied on Mon, 2008/02/04 - 4:08am
in response to: vaclav
vaclav replied on Mon, 2008/02/04 - 5:15am
Thank you, Guillaume, for clarification, I see your points.
Alex Tkachman replied on Mon, 2008/02/04 - 4:10pm
in response to: afsina
Just for protocol, as we say in Russian police...
We (Groovy community) put huge efforts to make Groovy as fast as possible by dynamic nature of the language and we already achieved huge progress compare to what we had in 1.0 and next version 1.6 has good chances to become much more faster compare to 1.5.x.