Closures are a widely used technique for writing functions in languages like Groovy and JavaScript. Oracle's JDK Roadmap add a convenient syntax for closures to Java in JDK 8 (some time in the future). This post describes how to use closures with the current JDK.
Closures -- functions plus their environment -- have been in Java for years, often listed in books under "Anonymous Inner Classes". For example, code like this has been around since JDK 1.1.
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// respond to button press
}
});
JDK 8 will lighten the syntax (not so many nested methods) with an enhancement to Java.
For today's (2011) JDK, there is a project called Commons Collections that assists in forming closures. The Commons Collections library is available here. I'm using both the 3.2 and the 3.2.1 RC 1 versions on projects.
Simple forAllDo Example
With Commons Collections' CollectionUtils class, the following syntax can be used to print out the contents of a list.
forAllDo(arraylist, new Closure() {
public void execute(Object obj) {
System.out.println(obj);
}
});
The forAllDo() function is statically imported which allows me to refer to the function without the class name pre-pended. For each element in "arraylist", System.out.println is called. The source file for the example is available at PrintListClosureTest.java.
A Closure with its Environment
In a more complicated example, this code will scan one list (a list of favorite sodas) for each element in another list (a list of available sodas).
forAllDo(availableSodas, new Closure() {
public void execute(Object _obj) {
Object fav = find(favoriteSodas, PredicateUtils.equalPredicate(_obj) );
if( fav != null ) {
System.out.println( _obj + " is a favorite soda");
}
else {
System.out.println( _obj + " is NOT a favorite soda");
}
}
});
find() is also statically imported. It is a static method of CollectionUtils.
The forAllDo() function will execute a block of code for each item in "availableSodas". The block of code also uses a closure "find()" which will attempt to find a match. The availableSodas example is more interesting because it references the list of favoriteSodas which is part of the environment. As such, it needs a special Java marking of "final" which prevents the favoriteSodas collection (or other variable) from being modified out from under the closure.
final List<String> favoriteSodas = new ArrayList<String>();
The listing for the available sodas example is available at MatchListClosureTest.java.
NOTE: There is an easy way of implementing the availableSodas example with both standard Java Collections' removeAll() and Commons Collections' CollectionUtils.intersection(). However, this use of closures -- with the availability of the environment -- can handle more complex cases.
Why Closures?
An alternative to closures is a standard static library, a class with static methods. This is useful for very general activities, but becomes unwieldy as new cases develop. I've seen applications form various Utils libraries, then add significant code trying to get various function arguments to conform. The availableSodas example works with strings, but if the data came from another data structure like beans, you'd have to convert to a list of strings to make a call like
PrintUtils.printAvailableFavorites(availableSodas, favoriteSodas);
Closure can be a convenient way to write functions, taking advantage of variables defined outside of the function (the environment). The closures in Commons Collections' CollectionUtils can head off a static library pattern and take advantage of object oriented techniques: the closures can be defined in their own class file. Like any coding technique, this is not a one-size-fits-all proposition. But use it if it helps.
References
For Oracle's JDK 8 Roadmap, follow this link.
Some syntax examples of what the JDK 8 Closure syntax might look like are here.
Thanks, very useful! I've been programming in Java for about 10 years, but haven't used the closures before.
ReplyDelete