| Closures have become quite popular recently as a good way to deal with collections
of objects. Here is what Martin Fowler has to say about closures: http://www.martinfowler.com/bliki/Closures.html
Closures are essentially function blocks to be executed on a subset of elements in a
collection. Another way to look at them is as being the middle part of a loop:
List coll = // ...
for(int i=0; i<coll.size(); i++) {
Object o = coll.get(i);
System.out.println(o);
}
Iterator iter = coll.iterator();
while(iter.hasNext()) {
Object o = iter.next();
System.out.println(o);
}
What I would define as the closure in both of these algorithms is, in fact, the
same:
System.out.println(o);
This is the heart of the concepts of closures - the algorithm for what is performed
on objects in collections is completely seperated from how you get the objects out
of the collection. While writing a simple for loop or a simple while loop isn't that
significant, iterating algorithms can get more complicated, the amount
of boilerplate code increases (as does the margin for error), and the amount of
code reuse decreases.
Another feature in Java that is a lot like closures is the
java.util.Comparator
class - the comparator is a novel idea of providing the algorithm for comparing
two objects without having to provide the sort algorithm itself - instead you can
simply call
Collections.sort(collection, [comparator implementation]);
-
which allows for the sort algorithm to be optimized to high-heaven and all you have
to worry about is optimizing the object-comparison algorithm itself; a very useful
seperation of concerns. Closures are the same thing for iterating collections - you
are essentially seperating the iteration algorithm from the functionality invocation.
The iteration algorithm may simply be looping, but it may also involve subsets of the
collection, filtering based on certain properties of the object, and more.
With Jakarta Commons Collections, closures are available in Java - and with them
you get a lot of built-in functionality. Most of the common closure functionality
you could need is built into a series of classes -
org.apache.commons.collections.Closure
,
org.apache.commons.collections.ClosureUtils
,
org.apache.commons.collections.Predicate
,
org.apache.commons.collections.CollectionUtils
,
org.apache.commons.collections.Tranformer
,
and
org.apache.commons.collections.TransformerUtils
.
To get started, here is how we implement our basic examples above using closures:
Collection coll = //..
CollectionUtils.forAllDo(coll, new Closure() {
public void execute(Object o) {
System.out.println(o);
}
});
Closures are invoked on every element in the collection when you call 'forAllDo'.
Arguably, this isn't much better than our simple for/while-loop implementations.
I plan to cover more of the closure features in the coming days - for now, here are some reasons
to consider closures:
- Closures can be used even when your not dealing with collections -
You may be iterating over elements in something as obnoxious as the Java XML DOM APIs -
where the index and retrieval methods are available, but there is no standard iteration
interface:
NodeList nodelist = elem.getElementsByTagName("item");
int len = nodelist.getLength(); // get length
Node node = nodelist.item(n); // get node for index
While there is no standard algorithm in the CollectionUtils class for iterating
a node list, you only have to write it once, and then pass in closures from then on:
public static void forAllDo(NodeList list, Closure closure) {
for(int i=0; i<list.getLength(); i++) {
Node node = list.item(i);
closure.execute(node);
}
}
-
Closures are full objects -
Closures aren't just algorithms, they
also can contain state in addition to the behavior.
Brian McCallister
just explained the advantage of this on his blog (you need to learn Scheme to understand what's going on!).
- Closures Can Be Reused -
I've already mentioned this - but it deserves
repeating - this is the strongest argument for using closures. Because they exist under a
common interface, they can be molded, altered, and reused without having to reimplement
blocks of code. In addition, filtering what objects are actually invoked on by
the closure can be done transparently of the closure implementation.
Next step now that I have tried to describe closures in a conceptual and simple implementation
standpoint will be to bring up some more complex examples of reusing the APIs in
collection commons - stay tuned.
Until next time,
R.J. Lorimer |