Approximating Java Case Objects without Project Lombok
2010/07/09 10 Comments
Over the past few months Dick Wall of the Java Posse has been talking up Project Lombok and for good reason. As he points out, its great for reducing boilerplate code in basic Java data objects. However, the more important point that Dick makes is that it prevents stupid errors, particularly when adding new object fields later on in the development cycle. The only unfortunate issue is that Project Lombok requires a compiler hack that can be rather confusing to those not using a supported IDE or for those coding outside of an IDE. However, Apache Commons provides an elegant solution to this issue.
Project Lombok
Lets start with a quick overview of Project Lombok. At its most basic level it provides a set of annotations (i.e. @Data) that you add to your Java data objects. You simply create private fields in an annotated object and the annotation will cause a full set of constructors, getters/setters, equals/hashCode and toString to be generated in the class file at compile time while keeping a simple and minimal source file. Its an elegant solution but it comes with a fair amount of magic surrounding it.
Alternatives
The simple alternative to Project Lombok is to have the IDE generate the code for you. Eclipse, for instance, is very good at doing this with auto generation of constructors, getters/setters, hashCode/equals and toString. This works wonders when creating the object for the first time but, as Dick points out, its too easy to add a new field and forget about updating toString and hashCode/equals leading to confusing and subtle bugs down the road.
The other alternative, and the one I am promoting, strikes a balance between Project Lombok and IDE generation by auto generating the hashCode/equals and toString and leaving the getters/setters and constructors up to the developer as these are necessary for the new field to be of any use.
Apache Commons
Buried deep in the Apache Commons is the builder package containing utilities such as EqualsBuilder, HashCodeBuilder, and ReflectionToStringBuilder. I don’t remember when I first discovered these gems but once I did the lightbulb went off; if used them in an Abstract base class they would ensure proper implementations of equals/hashCode and toString without having to mess with every data object I create.
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
public abstract class AbstractBaseObj {
@Override
public boolean equals( final Object obj ) {
return EqualsBuilder.reflectionEquals( this, obj );
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode( this );
}
@Override
public String toString() {
return new ReflectionToStringBuilder( this).toString();
}
}
Drawbacks
Of course this option is not with out its own drawbacks compared to Project Lombok. First off, as mentioned before, it does not deal with getters/setters or constructors. However, as I pointed out, I don’t believe this to be a huge imposition since creating a new field without creating getters/setters and/or a constructor using that field is fairly useless. Additionally, I like that this gives ease and flexibility over which constructors are created. This has been quite handy in the most recent project I’ve been working on.
Another draw back is that it requires an Abstract base class from which all objects must inherit. Annotations are certainly a more elegant solution since it doesn’t require a given object hierarchy. However there are often good reasons for a project specific base object for reasons beyond just equals/hashCode and toString. Additionally, the base class is fully owned by the project with just the few methods necessary being implemented. This is significantly better than having to inherit from a class provided by some 3rd party jar.
The important drawback, however, is that the Apache Commons solution uses reflection instead of actually generating code to implement equals/hashCode and toString. Depending on how frequently these are used and how performant they need to be this might be a real consideration to use either Project Lombok or to write all the code directly. However, I have not found this to be the case in my project as it never seems to show up as a hot spot in any profiling I have done.

I agree. I always found the excessive amount of “magic” involved in Lombok not worth the effort. I much prefer the apache method. I don’t think the drawbacks are that severe. You can always just throw those implementations into any class you need them if you don’t want to use up the super class. They are much cleaner than the alternative.
I’m curious if You know about “annotation processing” support in NetBeans 6.9? It makes using project Lombok as simple as referencing Lombok Jar in project properties. I know there still is some magic involved behind the scenes.
I think its cool that projects like Lombok get made because they are interesting and push the language forward, but I will probably never use it for exactly the reason you mention about Netbeans 6.9. If I have to assume that the person reading my code is using this tool AND is using a specific IDE in order to be able to see it… that’s just a complete loser for me. If you work on a large team you simply can’t make those kinds of assumptions. Even if you all happen to use the same IDE now, will you later?
I might consider it if I was working on a project by myself, but I would probably go with Groovy or Scala first, which gives you some of this and much much more, although it has its own drawbacks.
First of all, Lombok is not NetBeans only. Currently NetBeans and Eclipse are supported, as well as products based on Eclipse. IntelliJ support is unfortunately still missing but is high on the priority list.
So your main point is that people might not understand the way your code works if they don’t a Lombok supported IDE. But I don’t think you need an IDE to understand any code. Of course you need to understand the workings, but I’d argue that that’s the case for all annotation based frameworks.
I think that this problem would be solved if more people would actually use Lombok in their projects
Currently the adoption of Lombok is still progressing, so I invite you to review your decision in a few month.
Roel, I went to look at the Lombok.org video. Before this I’d only heard about it several times on the Java Posse. I think I changed my mind. I don’t think I’ll be partaking in the one that throws checked exceptions without declaring them, but the @Data one looks like a huge win. Eclipse was easy to set up and its easy to use. There were some concerns by my teammates about debugging the less trivial methods but using delombok makes that less of an issue.
Thanks for poking me. I’ll definitely try this out.
Clearly everybody has got his own standard, but after years of “liberation” from frameworks that mandated to extend a base class (such as EJB 2) I can’t imagine anything worse than having to extend a class… for equals()/hashcode()/toString(). It’s inheritance used for factoring out common code, and it’s a known anti-pattern.
I think that the superclass is a side note here and the main point is to have a ready made correct implementation of equals/hashcode and tostring. The correct part is less critical with tostring but handcoded equals and hashcode can have enormous and very hard to find bugs in them. Even the IDE generated ones are tough because you have to remember to update them. Having a nice, tight, useful implementation of these methods is really helpful. I would personally be ok putting these in the classes where I need them directly. I do that now but I do it with Eclipse’s generated ones which are like razor blades on the eyes.
These classes also have lots of customization if you want more specific behavior but the defaults are pretty good.
One other thing. I’m not entirely sure what you mean by inheritance used for factoring out common code is an anti-pattern. Isn’t that exactly what superclasses are? Logic that is common to all subclasses? I’m just not sure I got what you were going for with that thought.
I second what John said. As well, where in EJB2 you were required to inherit from someone else’s base class in this case its a class that you write. Additionally, as John pointed out, you can even put these inside your own class if you really don’t like the super class idea.
For those coding outside an IDE, Lombok supports the plain old java compiler javac, so no problem there. If you want to see the actual code Lombok generated you can use the delombok tool.
Awesome, I didn’t know of delombok.