09 Oct
Posted by: Matt in: functional-programming, groovy, java, loty
I spent the better part of yesterday tracing my way through the codebase for a large-scale enterprise application that my team is building right now, and I happened upon the following piece of code:
//...imports excluded for clarity
public class BusinessActivityBinMetaClassHelper {
//...
public static List<Long> getSrmMetaClassIdListJava(List<BusinessActivityBinMetaClass> businessActivityBinMetaClassList) {
List<Long> srmMetaClassIdList = new ArrayList<Long>();
if(businessActivityBinMetaClassList != null) {
for(BusinessActivityBinMetaClass businessActivityBinMetaClass : businessActivityBinMetaClassList) {
if(businessActivityBinMetaClass.getSrmMetaClass() != null && businessActivityBinMetaClass.getSrmMetaClass().getSrmMetaClassId() != null) {
srmMetaClassIdList.add
(businessActivityBinMetaClass.getSrmMetaClass().getSrmMetaClassId());
}
}
}
return srmMetaClassIdList;
}
//...
}
As I read this code, I thought “This just SCREAMS a need for Groovy’s closure iterators.” So, last night I quickly hacked out fully-equivalent Groovy version of the code:
class GroovyExample {
static def getSrmMetaClassIdListGroovy(def businessActivityBinMetaClassList) {
businessActivityBinMetaClassList?.collect { it?.srmMetaClass?.srmMetaClassId }.findAll { it != null } ?: new ArrayList<Long>();
}
}
Whew! Much nicer. What did we get out of this? Well…
Great - why don’t we just rewrite the entire application is Groovy? Well, hold on just a minute. At the time we started this application, as much as some of us loved Groovy, we just didn’t have enough Groovy mindshare to go there yet. On top of that, we were already experimenting with several new architectural ideas and technologies, and Groovy would have added yet one more risk to the puzzle. I say all this to acknowledge that sometimes you just can’t move to another language for one reason or another, regardless of how attractive its features may be.
But let’s take a queue from the Pragmatic Programmer and explore the LOTY (Language of the Year) concept one more time. One of the reasons that you’re encouraged to learn new languages is to change the way you program in your main language. You may learn Groovy, Scala, Clojure, Ruby, etc., etc., etc. and never use them in your day job - and yet the experience of coding in a new language with new constructs and idioms will necessarily change the way you THINK about programming in every other language.
So, let’s think about the possibility of coding something that is much more similar to the Groovy version and yet stick with regular Java code. Fortunately, there are several libraries out there that bring much of the flavor and power of Groovy’s closure iterators to Java. I’d like to focus in on one of them, LambdaJ (http://code.google.com/p/lambdaj/).
LambdaJ provides constructs that allow us to “…manipulate collections in a pseudo-functional and statically typed way.” Let’s take a look at this example implementing using LambdaJ:
//...some imports excluded for clarity
import ch.lambdaj.function.convert.Converter
import static ch.lambdaj.Lambda.*
import static org.hamcrest.Matchers.*
public class BusinessActivityBinMetaClassHelper {
//...
public static List<Long> getSrmMetaClassIdListJava(List<BusinessActivityBinMetaClass> businessActivityBinMetaClassList) {
return (businessActivityBinMetaClassList != null) ? filter(notNullValue(),convert(businessActivityBinMetaClassList, new IdExtractor())) : new ArrayList<Long>();
}
class IdExtractor implements Converter<BusinessActivityBinMetaClass,Long> {
Long convert(BusinessActivityBinMetaClass from) {
if (from.getSrmMetaClass() != null && from.getSrmMetaClass().getSrmMetaClassId() != null) {
return from.getSrmMetaClass().getSrmMetaClassId();
}
}
}
Not quite as nice as the Groovy code - we got A LOT out of the null-safe dereference and Elvis operators. However, our overall intent is still a bit clearer. Let’s analyze:
If you don’t count the Converter implementation, we’ve gotten ourselves down to 2 lines of code (1 if you don’t mind long lines). In this case I implemented IdExtractor as a named inner class - you could do this with an anonymous inner class and it would look a lot more like a closure, but the added noise of all of the null checking made the undesirable for me. Perhaps if your code has less noise (or guarantees that values aren’t null), that would be a better approach.
Another alternative is to make IdExtractor a top-level class that, if general enough, could be reused throughout the codebase. The benefit of this is that you now have a nice code unit rather than a battery of static methods in a utility class, and unit testing becomes much more clean and elegant.
So, we’ve still made some progress and made our code a bit more Groovy. I encourage you to explore LambdaJ’s feature set and see how it might make your code a bit more concise with clearer intent. And just to stir up a little controversy, look what would have happened in Java 7 had the null safe dereference and Elvis operator’s made the Project Coin cut:
//...some imports excluded for clarity
import ch.lambdaj.function.convert.Converter
import static ch.lambdaj.Lambda.*
import static org.hamcrest.Matchers.*
public class BusinessActivityBinMetaClassHelper {
//...
public static List<Long> getSrmMetaClassIdListLambdaJ(List<BusinessActivityBinMetaClass> businessActivityBinMetaClassList) {
return filter(notNullValue(),convert(businessActivityBinMetaClassList,
new Converter<BusinessActivityBinMetaClass,Long> {
Long apply(BusinessActivityBinMetaClass from) { return from?.getSrmMetaClass()?.getSrmMetaClassId()}
})) ?: new ArrayList<Long>();
}
}
Nice, huh?
6 Responses
Mario Fusco
10|Oct|2009 1Hi Matt,
My name is Mario Fusco and I am the main developer of the lambdaj project. Thanks a lot for your interest in my project. Actually your example could be a lot improved as it follows:
public static List getSrmMetaClassIdListJava(List businessActivityBinMetaClassList) {
. return (businessActivityBinMetaClassList != null) ?
filter(notNullValue(), extract(businessActivityBinMetaClassList, on(BusinessActivityBinMetaClass.class).getSrmMetaClass().getSrmMetaClassId())) :
new ArrayList();
}
So you can achieve what you need with just one line of code and without having any inner class. Indeed lambdaj does all the null checks for you. Almost better than Groovy, isn’t it
Let me know if it works.
Cheers
Mario
Matt
11|Oct|2009 2Mario,
Thanks for your response! This looks great. However, I’m having some problems getting it to run from my Groovy test class….here’s the result:
Caught: groovy.lang.MissingMethodException: No signature of method: $java.lang.Iterable$$EnhancerByCGLIB$$d94f4d3d.getSrmMetaClassId() is applicable for argument types: () values: []
at CollectExamples.getSrmMetaClassIdListLambdaJTwo(Collect.groovy:36)
at Collect.run(Collect.groovy:87)
Not sure what I’m doing wrong…any ideas?
Thanks,
Matt
Mario Fusco
11|Oct|2009 3Hi Matt,
sorry but I never tried to use lambdaj from a Groovy test class, so at the moment I don’t have a clear idea of what is going wrong. It seems that for some reason Groovy doesn’t work well with the cglib proxies generated by lambdaj and if this is the problem I am afraid there is no solution for it.
However could you send to my email the code you are using, so I can reproduce this issue by myself?
Just for curiosity did you try my solution from a plain Java test class? Does it work in that case?
Bye
Mario
Steve
12|Oct|2009 4Matt,
I think you can do what you want with just the core Java classes. First, I would make it an error for businessActivityBinMetaClassList to ever be null. You could use Collections.emptyList() instead.
You would create a utility class:
class FunctionalMap {
Converter func;
FunctionalMap(Converter c) { func = c;}
List map(List src) {
List val = (src.isEmpty()) ? Collections.emptyList() : new ArrayList();
for (I i : src) {
val.add(convert(i));
}
return val;
}
}
and use it like this:
FunctionalMap inToOut = new FunctionalMap(myConverter);
List myOutList = inToOut.map(myData);
Sorry about the formatting. With this approach I think you could even make an implementation using SynchronousQueue instead of a List for continuous conversion using multithreading.
There would be no need for the static method or helper class, just define inToOut where it is needed and call map.
Another approach might be to make FunctionalUtil and have a static map method that takes the converter.
Thoughts?
Steve
Sakuraba
12|Oct|2009 5While I like Closures and they way groovy decorates existing JDK classes with them, I think that most of the “hard-to-read”-ness in the original code stems rather from too long class/variable names and crappy formatting than from the absence of Closures.
Steve
12|Oct|2009 6I thought this trick would be needed:
Collections.emptyList() can not be used in the ternary as I have it without casting the return result so create a private function that returns Collection.emptyList() to replace the inline call.
Leave a reply
Search
Speaking Engagements
Memphis/Mid-South Java User Group
Real Developers Read! - Delivering Value
January 21, 2010 - 6 PM
Memphis/Mid-South Java User Group
Polyglot OSGi
April 15, 2010 - 6 PM
The Lambda Lounge
St. Louis, MO
Polyglot OSGi
February 4, 2010 - 6 PM
Mid-South Software Symposium
April 23-25, 2010
Memphis, TN
Northern Virginia Software Symposium
April 30-May 2, 2010
Reston, VA
Twitter
follow mstine at http://twitter.com
Categories
Archives
Blogroll
Leader of the Memphis/Mid-South JUG
Pandora
Meta
A design creation of Design Disease
Copyright © 2007 - Matt Stine’s Blog - is proudly powered by WordPress
InSense 1.0 Theme by Design Disease brought to you by HostGator Web Hosting.