read

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:

"Todas las charlas que menciona Clean Architecture <a href="https://twitter.com/hashtag/CleanHype?src=hash">#CleanHype</a> <a href="https://twitter.com/hashtag/DroidconMAD?src=hash">#DroidconMAD</a> <a href="https://t.co/gvr1yMqj8o">pic.twitter.com/gvr1yMqj8o</a>" — Antonio López Marín (@tonilopezmr) <a href="https://twitter.com/tonilopezmr/status/754348235150491649">July 16, 2016</a>

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 your new best friends, Refactor This and Generate

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:

  1. Marking the class final
  2. Like the @ToString annotation, generating a toString() with all the fields present
  3. Like the @EqualsAndHashCode annotation, generating valid equals() and hashCode() methods
  4. Like the @AllArgsConstructor annotation, generating a constructor with all the fields as arguments
  5. 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!

Blog Logo

Guillermo Orellana


Published

Image

Guillermo Orellana

May contain traces of sarcasm and siesta

Back to Overview