Introduction to Lombok Part One
Boilerplate Code is Painful
It's one of the biggest complaints developers have about Java, "There is too much boilerplate." To be honest I really like Java, but I have to admit that there is a lot of boilerplate code that you have to constantly repeat such as:
- Null checking
Thankfully modern IDEs such as IntelliJ and Eclipse can auto-generate a lot of this code for you using a few swift keyboard shortcuts. However there is still a problem; you have to see all that code. The boilerplate still exists, you didn't have to write it but you still need to see it and waste precious brainpower being distracted by it as you search for the real functionality.
This is particularly egregious in classes that are used for simple data storage commonly called POJOs (Plain Old Java Objects) whereby all you really care about is the instance variables but you need to see many lines of boilerplate as well.
Consider the simple data class below:
That's a lot of boilerplate for such a simple POJO, now imagine you have hundreds of POJOs and all of a sudden your project is full of boilerplate code that you need to see in code reviews and sift through to find the actual functionality of the class. But what if I told you using Lombok all that boilerplate could be reduced to what is in the class below, well it is possible and this article is going to introduce some of Lomboks most useful features that will save you time and code review effort.
As you seen in the example above @AllArgsConstructor does exactly what you would expect it to, it creates a single contructor featuring arguments for all the instance variables in the class. Later we will see how you can create constructors with a more restricted set of the instance variables using @RequiredArgsConstructor.
So let's see an all arguments constructor before using Lombok:
Now have a look at code with identical functionality except we used @AllArgsConstrcutor:
@Getter / @Setter
These two are very easy to explain and really require no demonstration (they were already covered in the introductory example):
- @Getter: Using this generates getter methods for each instance variable in your class. So if you have 1 instance variable called name then @Getter will generate the getName() method for you. Alternatively you can place @Getter on individual instance variables if you don't want to generate a getter for all your instance variables.
- @Setter: Using this generates setter methods for each instance variable in your class. So with the previous examples 1 instance variable called name then @Setter will generate the setName(String name) method for you. Alternatively you can place @Setter on individual instance variables if you don't want to generate a setter for all your instance variables.
This is to be used in conjunction with @AllArgsConstrcuctor and/or @RequiredArgsConstructor. Once you add a non-default constructor the default empty constructor is no longer available unless explicitly made so. That's exactly what @NoArgsConstructor is for, it generates a default empty constructor for you. Take a look at the before and after images below.
This is by far my favourite Lombok annotation as it helps deal with one of every developers pet peeves, the dreaded NullPointerException.
Specifically @NonNull can be used in two instances:
When used to annotate parameters @NonNull will automatically generate a check for null in the method before any other code, giving you a nice way of ensuring a null value doesn't get caught deep in your methods logic. It also serves a documentation purpose, as a developer if I inspect someone else's method and see parameters annotated with @NonNull I know to check I will not pass null to that method. This documentation benefit helps avoid other developers using your methods in ways you didn't initially intend.
The two methods below are functionally equivalent, though the second using @NonNull is much cleaner.
Marking instance variables with @NonNull does exactly what you might expect, it generates null checks anywhere the variable is being set. For example in constructors that feature the variable @NonNull will create a null check. An example is given below of two equivalent classes, one using @AllArgsConstructor with @NonNull and another using vanilla Java.
This is similar to @AllArgsConstructor with one distinct difference; only final instance variables and those marked with @NonNull will be included in the generated constructor. Those instance variables marked with @NonNull will also have the null check generated as we seen previously in the @AllArgsConstructor example.
Please see the example below to see what instance variables get included in the generated @RequiredArgsConstructor and which don't.
The builder pattern is a very common design pattern in Java for classes that contain a lot of instance variables, however when implemented in a non-Lombok way it is ugly, you need a static inner class and anytime you add a new instance variable the builder class also needs updated. This leads to literally hundreds of lines for a very repetitive implementation.
The @Builder annotation from Lombok reduces all those lines into one single annotation, allowing you to stop worrying about maintaining your manually coded builders and get on with the important programming tasks! To see the impact of the @Builder annotation all you need to do is look at the example below.
Learn More About The Builder Design Pattern
- The Builder Design Pattern
The builder design pattern allows for more descriptive object construction using a Builder object and chained setters. This design pattern is perfect for when you have objects that require a lot of parameters.
Whether you're stuck debugging a program using system print calls or need to log important details of objects you are processing we have all needed to use a well implemented toString() method.
I have even seen some developers serializing objects to JSON strings in order to log them so all the variables and their values are listed. This is an expensive operation generally requiring external libraries like GSON or Jackson.
We can achieve the same outcome with a single Lombok annotation; @ToString. This annotation can be configured to exclude certain member variables by annotating them with @ToString.Exclude. You can make it call the toString() method of it's superclass as well using @ToString(callSuper=true) and you can make it include all the field names using @ToString(includeFieldNames=true).
I won't add an example of this annotation to this article because it doesn't fit nicely into images due to the line length of the equivalent toString() method when implemented in vanilla Java. However you can follow this link to the official Lombok @ToString documentation.
End of Part One
So we have covered some of Lomboks best boilerplate reducing annotations, in part two we will dive in to some more boilerplate reduction using:
However we will also dive into some more advanced Lombok features such as:
I look forward to seeing you in part two and hope this has served as a good introduction to how to reduce boilerplate Java code using Lombok annotations.