Lombok Tutorial: Code Less, Do More

Lombok is a popular Java library that aims to reduce boilerplate code by providing annotations that generate getters, setters, constructors, and other commonly used methods automatically during the compilation process. In this tutorial we will learn how to use it and which are the pros and cons of this library.

How to install Lombok

In order to get started you need a couple of things. At first you need to add the Lombok library to your project. If you are using Maven:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency>

Next, you need to install the Lombok plugin your for IDE so that it can find the “hidden” boiler plate code which is created with Annotations. For example, if you are using IntelliJ IDEA, install the Lombok plugin from the Settings of your Project:

Java Lombok tutorial

Much the same, if you are using Visual Studio you can include a Lombok Extension for Visual Studio:

This extension provides support for using Lombok Annotations in your code and to revert back to standard coding, if you want to remove Lombok from your code.

Writing your first Lombok example

Then, you are ready to add annotations in your code. Let’s see an example class:

public class Person {
    String firstName;
    String lastName;


    public static void main(String[] args) {

    }
}

We would typically need to add getter/setters method to our POJO, along with Constructors with fields, toString() method and overriding equals(). All this can be done in one shot by adding the @Data annotation to the class:

import lombok.Data;

public @Data
class Person {
    String firstName;
    String lastName;


    public static void main(String[] args) {

    }
}

As you can see, even though your Class does not contain the boiler plate code, you can actually use the getter and setter methods:

Speed up your Java coding with Lombok

Some useful Lombok annotations

Besides the @Data annotation which adds a bulk of boiler-plate code, you can choose to add just single methods, like a Constructor with all args, using @AllArgsConstructor

@AllArgsConstructor
public class Person {
    String firstName;
    String lastName;


    public static void main(String[] args) {

    }
}

You can then add also getter and setters method using the @Getter and @Setter annotations:

@AllArgsConstructor
@Getter @Setter
public class Person {
    String firstName;
    String lastName;


    public static void main(String[] args) {

    }
}

Much the same thing, you can add the equals method with @EqualsAndHashCode and the toString method using @ToString

Avoiding the Null Pointer Exception trap

As you certainly know, in order to avoid Null Pointer Exceptions in your code you often end up including boiler plate code in your classes that check if fields/parameters are null or not. This can be avoided using the elegant @NonNull annotation. Here is an example:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

The @NonNull looks like if (param == null) throw new NullPointerException(“param is marked @NonNull but is null”); and will be inserted at the very top of your method. For constructors, the null-check will be inserted immediately following any explicit this() or super() calls.

Adding Design Patterns using Lombok Annotations

Another areas where Lombok can be pretty useful is the Design Pattern area. For example, consider the Builder pattern that allows us to write readable, understandable code to set up complex objects.You can include static builder method with the @Builder annotation:

    @Builder
    public class Person {
    	String firstName;
    	String lastName;

    	public static void main(String[] args) {
    		Person emp = new PersonBuilder().firstName("John")
    				.lastName("Doe")
    				.build();
    	}
    }

Another Pattern that you can implement with Lombok is the Delegate pattern. Any field or no-argument method can be annotated with @Delegate to let lombok generate delegate methods that forward the call to this field (or the result of invoking this method).

Lombok delegates all public methods of the field’s type (or method’s return type), as well as those of its supertype except for all methods declared in java.lang.Object.

You can pass any number of classes into the @Delegate annotation’s types parameter. If you do that, then lombok will delegate all public methods in those types (and their supertypes, except java.lang.Object) instead of looking at the field/method’s type. Here is an example:

import java.util.ArrayList;
import java.util.Collection;

import lombok.Delegate;

public class DelegationExample {
    private interface SimpleCollection {
        boolean add(String item);

        boolean remove(Object item);
    }

    @Delegate(types = SimpleCollection.class)
    private final Collection<String> collection = new ArrayList<String>();
}


class ExcludesDelegateExample {
    long counter = 0L;

    private interface Add {
        boolean add(String x);

        boolean addAll(Collection<? extends String> x);
    }

    @Delegate(excludes = Add.class)
    private final Collection<String> collection = new ArrayList<String>();

    public boolean add(String item) {
        counter++;
        return collection.add(item);
    }

    public boolean addAll(Collection<? extends String> col) {
        counter += col.size();
        return collection.addAll(col);
    }
}

Simplify Logging with Lombok

Another area where we have to create boiler plate code is Logging, by placing a static final log field in each class, initialized with the name of the class. This can be avoided by putting the variant of @Log on your class (whichever one applies to the logging system you use). Here is an example:

import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

@Log
public class LogExample {
  
  public static void main(String... args) {
    log.severe("severe error!");
  }
}

@Slf4j
public class LogExampleOther {
  
  public static void main(String... args) {
    log.error("error!");
  }
}

@CommonsLog(topic="CounterLog")
public class LogExampleCategory {

  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

Here is the list of available Log annotations:

  • @CommonsLog: Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
  • @Flogger: Creates private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
  • @JBossLog: Creates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
  • @Log: Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  • @Log4j: Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
  • @Log4j2: Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
  • @Slf4j: Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  • @XSlf4j: Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

When to use Lombok and when not to use it

While Lombok can be beneficial in many cases, there are scenarios where its usage might not be ideal. Here’s when to consider using Lombok in your Java projects and when it might not be suitable:

When to use Lombok:

  1. Reducing Boilerplate Code: Lombok shines when it comes to reducing repetitive and verbose code, especially for simple value objects or entities. It can save development time and enhance code readability by generating common methods automatically.
  2. Maintainability and Readability: By removing the need to write and maintain getter/setter methods, Lombok can make your codebase more concise and easier to read. It reduces the risk of introducing errors during manual method creation or modification.
  3. Code Consistency: Lombok helps enforce consistent coding standards across your project by generating methods with standardized names and formatting. It ensures that commonly used methods follow the same conventions.
  4. Integration with IDEs: Lombok has strong integration with popular Java IDEs like IntelliJ IDEA and Eclipse. IDEs recognize Lombok annotations and provide proper code completion, navigation, and refactoring support.

When not to use Lombok:

  1. Codebase Compatibility: If you are working on a project with an existing codebase that does not utilize Lombok, introducing Lombok might introduce a learning curve for new developers and increase the complexity of code reviews and maintenance.
  2. Team Consensus: Before adopting Lombok, it’s essential to consider the preferences and familiarity of your development team. If team members are not comfortable or experienced with Lombok, it may lead to confusion or difficulties in collaboration.
  3. Debugging and Troubleshooting: Lombok-generated code can make it harder to debug issues or track down errors since the actual code executed might differ from what you see in the source files. Understanding the generated code can be challenging for developers unfamiliar with Lombok.
  4. Code Generation Overuse: While Lombok offers convenience, excessive reliance on code generation can lead to bloated classes and diminished code maintainability. It’s important to use Lombok judiciously and consider the impact on the overall codebase and project complexity.

In summary, Lombok can be a valuable tool for reducing boilerplate code, enhancing code readability, and improving development efficiency. However, careful consideration should be given to factors such as team consensus, codebase compatibility, and debugging needs before adopting Lombok in a project. Assess your specific project requirements and consider the trade-offs to make an informed decision.