Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does simulation of closures in Java make sense?

Tags:

java

Languages that have closures, such as Ruby, enable elegant constructs to transform lists. Suppose we have a class

class QueryTerm {
  String value;
  public String getValue() {...}
}

and a list of terms List<QueryTerm> terms, that we want to transform into its list of values List<String> values.

In Ruby we could write something like:

values1 = terms.collect do |term|
    term.getValue()
end

Java forces us to construct the result list and iterate through the terms collection ourselves (at least there is no iterator or index position involved since foreach was introduced):

Collection<String> values2 = new HashSet<String>();
for (QueryTerm term : terms) {
    values2.add(term.getValue());
}

Apache Commons and Google Collections2 try to emulate closures in Java by using anonymous classes:

Collection<String> values3 = Collections2.transform(terms, new Function<QueryTerm, String>() {
    @Override
    public String apply(QueryTerm term) {
        return term.getValue();
    }
});

The weird thing is, that this version has more lines of code and more characters than the original version! I would assume it is harder to understand for this reason. Therefore I dismissed the idea back when I saw it in Apache Commons. However, having seen it introduced in Google Collections recently I am starting to wonder if I am missing something.

Therefore my question: What is your opinion of the constructs above? Do you consider the Collections2.transform() version more readable/understandable than the default one? Am I missing something completely?

Best regards, Johannes

like image 402
Joe23 Avatar asked Nov 27 '09 12:11

Joe23


1 Answers

Closures as used here are pretty much the same thing as the Command pattern. I'm guessing any of the reasons commands can be useful apply here. A Command could also be slightly more full fledged than a closure of course, since the latter is only on the function level. But a key observation is that this is often all you need.

There's a couple of advantages that are immediately obvious to me:

  • Functional style of declaring your operations maps more easily to our way of thinking about them, i.e. here's my function, please apply it to this data.
  • Chaining of transformations happens through transparent use of different calls to apply/filter/transform. You can just keep chaining them after each other and you see all the operations collected after each other in one line. Sure there's lots of boilerplate around it still, but if done right it's eminently readable to any java programmer.
  • Inversion of control: Say you want to decompose your operation over several different threads. If you wrote your own loop you're now stuck: you have to decompose it yourself into several different loops. This is usually ugly and tricky to write code, and it's going to require you to create several Runnables which you pass off to other threads. So really you're just working with closures all over again.

    One of the JDK7 proposals that won't make it was apparently something that did exactly all this for you, where you just submit your data and define what filters to perform on it, and you let it go its merry way. You can actually download the code for this (package extra166y). All the ops you can see in the javadoc for this package could be eliminated and replaced by closures. So this is a good example why a certain approach fails because of lack of real closures, and "simulating" closures doesn't quite cut it anymore.

Really, there's tons of problems that you can really easily solve by just passing in a simple function that does what you want it to do to the data and let a framework fill in all the rest. You can do this right now with anonymous classes, but proper closures would at least cut away a lot of the boiler plate.

One of the frameworks I've used that uses something like this approach to cut away a lot of the boilerplate is Spring-Jdbc. Here you pass the JdbcTemplate a simple function that grabs your objects out of the ResultSet and you pass it along with your query to template.query(). This cuts out a lot of aggravation and the unreadability of the usual JDBC code. A definite win if you'll be maintaining anything like this.

like image 186
wds Avatar answered Sep 17 '22 16:09

wds