Lombok: Spice up your Java

This is the first instalment in my Code More Efficiently series. Of all the open source libraries mentioned, I believe Lombok to be the easiest to use. It is almost effortless, which makes it a good starting point. The demo code for this guide is on Github. I also have a few slides for this  guide.

Update: I’ve just pushed the final demo code to Github. I couldn’t think of a better way to demonstrate the functionality than unit tests.

What is Lombok?

Lombok is an open source library to help reduce all the boilerplate in Java code. Lombok does this through the various annotations that it defines. Lombok’s annotations apply to one or multiple elements of your code such as classes, methods, variables, method parameters, and more. These annotations allow you to

  1. Tell Lombok to generate a specific piece of code
  2. Specify options governing the code generation (if any)

This guide won’t go into too much detail and I won’t be discussing all of Lombok’s annotations. If you’d like to read more, head over to Project Lombok’s website for a full guide. Lombok is Open sourced under the MIT License and the source code can be found on
Github.

But Why?

You might be thinking “Why should I let some library generate some invisible code for me?”, and that’s a good question. Hopefully by the end of this section you’re convinced that it’s a good thing.

Think about your average Java object. You’ll need to consider at least a few of the following:

  • Constructors initialize your instances. They’re rarely complex, but often repetitive.
  • Builders allow you to shrink your constructors while still letting you fully initialize an instance.
  • Getters and Setters are ridiculously simple to write, but aside from the type of the field, you’re typing the same things over and over and over.
  • Object Boilerplate (toString, equals, hashCode, etc) is often crucial to the correct behaviour of your classes (e.g. if you don’t implement hashCode, you can forget about putting your instances in a HashMap).
  • Releasing Resources via the try/finally approach can get downright annoying … specially when you realize your code is hitting the fan because you forgot to clean up after yourself!

Are you starting to see a pattern? A lot of typing for too little functionality is not productive use of your time. These are all more often than not trivial to write, but you have to actually write them. Again, They’re not difficult, just time consuming. You won’t have to implement all of the above for every single class you code, but I’m sure we agree that they have to be done. We agree that they have to get done, but… say it with me…

The grabage man can!
Can’t someone else do it?

Of course, Lombok can!

Null Checks

You can annotate your member variables with @NonNull, which tells Lombok that this field may not be null. Lombok automatically adds a null check when generating a constructor for you.

Constructors

Lombok can generate your constructors for you. Of course if there’s anything other than your basic initialization required, you’ll have to do that yourself.

Constructors Without Arguments

You can annotate your class with the @NoArgsConstructor to have Lombok generate your no args constructor for you. Here’s a simple example:

public class Foo {
    // your vars

    public Foo () {
        // init your vars to default values
    }
}

Can be shortened to the code below

@NoArgsConstructor public class Foo {
    // your vars
}

Keep in mind that depending on the contents of your class, this annotation may not be able to generate a constructor for you. For example if you have any fields that are marked as @NonNull, Lombok cannot generate a valid no-argument constructor for you. Doing so would require Lombok to assign a value to your field (i.e. code for you).

Constructor To Initialize All Fields

At the other end of the spectrum, you have the @AllArgsConstructor annotation, which (as you can guess) generates a constructor for you that takes an argument for all non static and uninitialized final fields. The order of the constructor parameters is the same order in which the fields appear in the class.

Constructor to Initialize Required Fields

You have the extremes, but you still need a happy medium. The @RequiredArgsConstructor is it. This annotation generates a constructor for you that initializes all @NonNull, non-static and uninitialized final fields.

Builders

Builder classes are similar to constructors. They allow you to reduce the number of constructor arguments while still allowing you to assign initial values to an instance. Another benefit of builders is their fluid syntax via chained methods (if the author decides to do so). The down side with builders is that they’re tedious to write. Here’s a simple example: The tiny bit of code below allows you to do something as amazing as the piece that follows it:

@Builder
public class Person {
    private           String      name;
    private           int         age;
    @Singular private Set jobs;
}

Now you can write something as awesome as this:

final Person adam = Person.builder()
        .name("Adam Savage")
        .age(47)
        .job("MythBusters")
        .job("Unchained Reaction")
        .build();

The @Singular annotation tells Lombok that you don’t want a Set as the input, but you want it to initialize an empty set and populate it with the input parameter after every call to the job(String) method. If you don’t think that’s amazing, you have to leave now. Of course if you wanted to provide the set instead, you’d remove the @Singular annotation.

Getters and Setters

Your class variables need Getters and Setters. These are very simple to write, but aren’t you already annoyed at having to type pieces like below:

public void setFoo (final int foo) {
    mFoo = foo;
}

public int getFoo () {
    return mFoo;
}

for every member variable of your class?

I pitty the foo who still writes setters and getters
I pitty the foo who still writes setters and getters!

Lombok can generate getters and setters for you either at a class level (for all non-static fields) or on a per field basis.

Getters

The @Getter annotation tells Lombok that you’d like some Getter methods generated. You can apply this annotation to a class to generate Getter methods for all non-static fields in that class; or you can individually annotate fields with @Getter to select which fields have getters generated for them.

Setters

The @Setter annotation works the same way with one (hopefully obvious) exception: When applying the @Setter annotation to the class, it will generate a setter for all non final fields. Applying the @Setter individually to a final field should generate a compile-time error.

Boilerplate Generation

In order for your classes to behave properly, you must obey certain unenforced rules. Let me clarify what I mean by unenforced: It won’t crash immediately, but it will eventually throw an exception that you may or may not be catching or otherwise misbehave. These rules are often simple ideas, that quickly get repetitive. Among said rules are try/finally blocks, the equals implementation, the hashCode implementation, and more.

Cleaning Up After Yourself

If you use a resource like an IO stream, you’re expected to close() it when you’re done. On Android, if you’re using a TypedArray you’re expected to recycle() it when you’re done. Since the eventual cleanup step is rather important, these are often done via try/finally blocks. Lombok offers an easier way via the @Cleanup annotation. The @Cleanup annotation automatically calls close() on the object instance that it is applied to once the instance is about to go out of scope. The following two code examples are functionally identical:

private void readStream () {
    final InputStream in = open ();
        // do something with the stream
    try {
    }  finally {
        in.close ()
    }
}

and

private void readStream () {
    @Cleanup final InputStream in = open ();
    // do something with the stream
}

Which would you rather write? I’d pick @Cleanup any day.

Say My Name!

Another extremely useful but extremely boring standard object method is the toString() method. A good toString() is extremely useful in logging debug and error conditions. Do not leave home without it. The good news is that Lombok can do that for you too. If you guessed @ToString to be the magical annotation that generates a human readable representation of your class, you’d be right! By default, all non-static fields are included, but Lombok allows you to exclude fields via the exclude parameter or use the of parameter to select which fields are included in the output string. If your class inherits from another, you can use the callSuper parameter to include or exclude the parent class’ toString() in the output string.

Equality Matters!

The concept is simple: two object instances are equal if they reference the same memory location. WRONG!! However if you don’t override the equals() method, that’s what Java gives you. Now there’s another method here that matters in instance equality. If two instances a and b of a class are equal, their hashCode() methods should also return the same value. This becomes especially important once you have to store your objects in standard Java collections such as List, Set, Map, and many others. If your class does not correctly implement the equals() and hashCode() methods you will experience problems with collections such as

  • false-negatives in
    • contains(Object)
    • containsKey(Object)
  • false-positives in
    • contains(Object)
    • containsKey(Object)
  • Undetected duplicates in Sets and Maps
  • And other oddities that (take my word for it) you don’t have the time and/or the patience to deal with

But fear not, as Lombok can generate that for you too! As with everything else we’ve seen so far, the idea behind the equals() and hashCode() methods is rather simple, but they’re just a pain to write. Use the @EqualsAndHashCode annotation to have your methods delivered to your code instantly.

Synchronize This!

The synchronized keyword allows you to ensure that certain pieces of code are executed by only a single thread at a time. Synchronized methods lock on this, which exposes the lock to the outside. The @Synchronized annotation instead locks on a private field named $lock, which is not accessible to outsiders. Static methods annotated with @Synchronized will lock on $LOCK. The fields $lock and $LOCK are generated if they don’t already exist. Alternatively you can specify the name of the field for the @Synchronized annotation to lock on, but Lombok will not generate the field for you. For example if you annotate a method as @Synchronized ("myLock"), you need to declare and initialize a private final Object field, named “myLock”.

Who Likes Shortcuts?

Yo Dawg, I heard you like shortcuts, so I made a shortcut to your shortcuts!
Yo Dawg, I heard you like shortcuts, so I made a shortcut to your shortcuts!

Just in case all the shortcuts above weren’t enough, Lombok has shortcut combos: You annotate your class with one annotation and you automatically get a few of lombok’s features enabled automatically.

Data Classes

The @Data annotation is a convenient shortcut that equates to @ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor. To quote the Lombok website:

“In other words, @Data generates all the boilerplate that is normally associated with simple POJOs (Plain Old Java Objects) and beans: getters for all fields, setters for all non-final fields, and appropriate toString, equals and hashCode implementations that involve the fields of the class, and a constructor that initializes all final fields, as well as all non-final fields with no initializer that have been marked with @NonNull, in order to ensure the field is never null.”

Value Classes

The @Value annotation is the immutable variant of @Data. To quote the Lombok website again:

“all fields are made private and final by default, and setters are not generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Like @Data, useful toString(), equals() and hashCode() methods are also generated, each field gets a getter method, and a constructor that covers every argument (except final fields that are initialized in the field declaration) is also generated.”

Thoughts?

That’s it. I can’t believe you actually stuck around to read up to here, but I hope you found this guide useful. If you have any thoughts, input, or corrections I’d be happy to hear them.