We all know that one of the most annoying things in Java is the thing we need to place lots of boiler plate code when building our applications. Think for example of adding Constructors with all fields, getter and setter methods, Logger static methods, not to mention the boilerplate code to use common patterns in your Java code.
Although IDEs are able to add boiler plate code so you don’t have to actually write that code, you still have to perform maintenance for example, if field names change, if you add or remove them. Also, a class with lots of fields becomes bloated with code, making it more complicated to read.
By using project Lombok you can include some simple annotations in your code that will, under the hoods, place all the boiler plate code in your classes.
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.6</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:
Once that Lombok plugin has been installed, 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 if no boiler plate code has been added to the class, all the stuff is actually available and ready to be used:
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); } }
Logging easier 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);
Checkout the Lombok project for more information: https://projectlombok.org/