This blogpost is from 2016. If you are reading this today, there are way better solutions than Lombok, for instance Kotlin
Looks like nowadays, clean is the trend. Clean eating, clean lifting, clean architecture…
Let’s have a look at the list of topics for the latest DroidCon in Spain:
Notice that there is a check mark for every talk mentioning or speaking about Clean Architecture. And opposite to what it might look like, the MAD in the name is not because they are MAD about Clean Architecture, but because it took place in Madrid. It is everything but a new concept, but seems to have drawn a lot of attention in the Android community during the past few months.
With such a huge resonation in the community, it must be our NextSilverBullet™, let us make all our code life and things clean! Layers of abstraction, pluggable, replaceable and testable components, immutable data models, one data model per layer…
So after x time refactoring or rewriting (where x approaches infinity), we end up with 10 times more boilerplate than actual code. How did we get here?
The pain
Java is a very flexible language. Some of the concepts in Clean Architecture introduce a lot of restrictions and requirements. Such restrictions and requirements need code to be expressed in Java. Other languages may not need so much code since they allow you (or require you) to be as restrictive (like, Kotlin and immutability), but if it’s Java what you have in your toolset, for instance to achieve immutability you need to define all args constructors, setters and builders to spice it up. Stuck with boilerplate you are. Or are you?
Meet Project Lombok
Project Lombok is magic made code. Or code that makes magic. Or… Well, it’s actually an annotation processor that generates code, but instead of creating derivate clases we have to guess (I am looking at you, Dagger2), it injects the generated code in our class. Told you, Magic!
How does it achieve its magic properties? Well, Lombok is not your typical annotation processor. Its annotations are processed at development time, injecting code so it’s immediately available for the developer. This requires some sort of collaboration from the IDE though.
Project Lombok and Android
In order to use it in you Android project, head to the project download webpage and get the coordinates to the latest version. If you are using the Gradle apt plugin, you might need to add its entry as well. It might look like this:
provided "org.projectlombok:lombok:1.12.6" // or latest version
apt "org.projectlombok:lombok:1.12.6" // only if using gradle-apt
Project Lombok needs little to no configuration, but there are some special cases we need to consider when using it in Android. In order to customise the behaviour of Lombok, we can put a file named lombok.config
in one of the roots of our project. I usually have it at the root of the module or modules that actually make use of Lombok, since we might choose not to use it in all of them.
In order to comfortably develop with it, you will want to add a plugin that supports Lombok generation at dev time. In Android Studio, this can be done installing the Lombok plugin, which is available from the plugins menu.
Generating way too many things
java.beans.ConstructorProperties
Android lacks of the beans package (thankfully), so when you try to use one of the constructor family annotations, you will get a compile time error similar to this one:
Error:(9, 1) error: cannot find symbol class ConstructorProperties
In order to disable such annotations from generating, just add this line to your lombok.config
:
lombok.anyConstructor.suppressConstructorProperties = true
javax.annotation.Generated
Yes, there is an annotation to say that something was generated by an annotation processor (Inception sound). Of course, there is a reason for this: Annotation processors are applied in rounds, so the output of one annotation processor may be processed by other annotation processors or even by itself on further rounds! (more Inception sound). In order to avoid going too many levels deep, generated code is annotated so the processors can distinguish it and decide wether process it or not.
The problem is, Android does not bundle this one either. You can go for the easy solution, which is not generating them at all:
lombok.addGeneratedAnnotation = false
Or the probably correct one, which is adding a dependency that contains this annotation to your build. Don’t worry, it’s retention is SOURCE
, which means it won’t go to your class files (and eventually your DEX files).
provided "javax.annotation:jsr250-api:1.0"
Comparison
Probably my favourite comparison to do is the use of the @Data
annotation. Consider this example taken from the Lombok project page:
@Value
public class ValueExample {
String name;
int age;
double score;
protected String[] tags;
}
Just one annotation, @Value
, will trigger:
- Marking the class
final
- Like the
@ToString
annotation, generating a toString() with all the fields present - Like the
@EqualsAndHashCode
annotation, generating valid equals() and hashCode() methods - Like the
@AllArgsConstructor
annotation, generating a constructor with all the fields as arguments - Like the
@Getter
annotation, generating a getter for each field (the@Getter
annotation would be required at each field otherwise)
So it’s like a super-annotation (or a shorthand for all the above). This would generate the following Java code. 70 lines of code generated by Lombok after writing just 7 lines. That’s 10 times the amount of code!
Alternatives
Is Lombok all what is out there? Is this a crazy isolated idea? Not really, there are many other options. Let’s explore two: first, a more moderated approach to code generation, without injection. Second, if you would rather turn it up to 11, a way to inject code for even more things.
AutoValue
@AutoValue
abstract class Animal {
static Animal create(String name, int numberOfLegs) {
return new AutoValue_Animal(name, numberOfLegs);
}
abstract String name();
abstract int numberOfLegs();
}
If you want a more traditional and less hacky experience, Google’s AutoValue does that for you. However, since it does less magic tricks, it does not feel nearly as easy to use. You need to declare your classes abstract and your values as abstract methods. It still allows you to reference the original class when instantiating, but you will work with a derived type.
Android Annotations
@Fullscreen
@EActivity(R.layout.bookmarks)
@WindowFeature(Window.FEATURE_NO_TITLE)
public class BookmarksToClipboardActivity extends Activity {
@ViewById
ListView bookmarkList;
@ViewById
EditText search;
@App
BookmarkApplication application;
@SystemService
ClipboardManager clipboardManager;
@Click({R.id.updateBookmarksButton1, R.id.updateBookmarksButton2})
void updateBookmarksClicked() {
//[...]
}
}
If code being injected is not a scary idea for you, but rather an exciting one, then why not inject all the things? Taking the concept of Project Lombok to the next level, with Android Annotations you can do things like @SystemService
to inject services from the current context. A bit too much for me, but always worth the try.
It even comes with a REST client that reminds me a lot of Retrofit:
@Rest("http://www.bookmarks.com")
public interface BookmarkClient {
@Get("/bookmarks/{userId}?search={search}")
Bookmarks getBookmarks(@Path String search, @Path String userId);
}
Conclusion
Project Lombok is a very powerful tool, and like all powerful tools it can get out of hands pretty quickly. But if you keep its usage down to a reasonable level, it can help you a lot to go through the most tedious parts of elaborated architectures: boilerplate code.
And if at some point you decide to get rid of Lombok, you can rest assured, they provide a system to de-Lombok your code and make the generated code persist instead of the annotations.
I am personally using this tool among others to help me in my rewrite of Travis for Android, more on that task to come soon!