In the previous post about Lombok library, I have described a library that helps to deal with boilerplate code in Java (and yes I know that these problems are already solved in Kotlin, but this is real life and we can’t just all sit and rewrite every existing project once a newer or a simpler language appears). But as many things in life, project Lombok has its alternatives. Let’s give them a chance as well.
It is really an alternative to Lombok – because you can’t use both at once. Or, at least it turns out that you will have difficulties while using both in the same project with IntelliJ IDEA, which is the IDE of choice for many and yours truly – because the two libraries deal with the annotation processing differently. So, neither can live while the other survives, which is approximately how a prophecy for Harry Potter and Voldemort sounded.
So, we already know how the Person class looked with Lombok annotations:
If we create a new project and make it use autovalue as described here, we can imitate pretty much the same model with AutoValue Builders.
Now let’s see how the AutoValue model looks:
What you can see is, there’s definitely more code.
While Lombok generates a builder with a single annotation, AutoValue will make you create your own builder code – not all of it though. Basically you define your interfaces and the implementation is left to AutoValue generated code, you don’t have to actually implement the code that is in getters and setters. Even if we agree that the AutoValue getter interfaces won’t take much more time or space than the Lombok field definitions, for some people it might still be a hassle and an annoyance to write the AutoValue builder code.
However, it allows for greater flexibility, because you can actually change the builder method names. Also, a big win is code analysis and usage search – this way, you can actually look for usages of actual getters and setters separately, which might also be important for developers.
The instance is created in the same way as with Lombok.
All our tests run with minimal code changes, mostly because AutoValue doesn’t have a way to transform an instance to a builder (or at least I could not easily find it), so copying is just calling a static factory method:
Other differences that are immediately obvious:
- AutoValue classes written by you are always abstract. They are implemented in AutoValue generated code.
- AutoValue classes are automatically immutable. There’s a workaround for having them have properties of immutable types. Even if you explicitly wanted to have setters on your instances, you can’t.
The library also uses Java annotation processors to generate simple, safe and consistent value objects. Well, same as the previous two. What else is new? Let’s see.
The simplest value class would look like this.
So, there’s the same principle of having abstract classes, that are only implemented in the generated code. For that, you need to enable the IDE annotation processors, same as you do for Lombok (but not for AutoValue, as there it is done by a gradle plugin).
How does the object creation look, then?
The most obvious differences are, at first glance:
- We don’t declare the builder methods.
- The static builder/factory methods are created not on our own class, but on the generated one.
- Same as AutoValue, there’s no way to generate setters on the class, just on the builder.
- The generated class also automatically adds with-ers, that is, instance methods, that allow to create a copy of the instance by changing one property:
- The builder has an automatically added from() method, that allows to create an exact copy of the instance, and there’s also a generated static copyOf() method on the generated class:
And again, our test runs with minimal changes, which are mainly about how we copy the instances:
There’s much more to say about Immutables library, so there’s a pretty big manual for it here. Here in this article we only scratched the surface a little. There’s for example much more details about JSON serialization with Immitables and style customizations (method prefixes, builder names etc.) and even repository generation for Mongo so that documents could be treated as immutables. But that is all much more than I care for to touch in this simple article.
The takeaway is, one of the challenges of the not-going-anywhere-yet Java language is verbosity and boilerplate code. But there’s numerous tools to deal with it, and one can choose a library that fits best, instead of coding by copy-paste or trying to write your own code generator.
Use them well.