Lombok: Remove boilerplate code from Java source files
This article is originally published at https://www.bugdbug.com/post/lombok-remove-boilerplate-code-from-java-source-files
Project Lombok is the boilerplate code generator library for Java. It autogenerates Java bytecode in .class files for different annotations used, during the build process.
It is a very popular framework among open source community and is a part of many open source projects. We can customize different annotations at the project level and define it’s behaviour so that individual developer does not have to worry about it. This also brings consistency to our code throughout the project.
Now, let's dive into its awesomeness.
First, we would need to add the following dependency in our maven project
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
Let’s see few widely used annotations and their vanilla Java code snippets.
1. NonNull
NonNull can be used to mark parameters that can not be null. The validation code for that is added by Lombok during compilation.
Vanilla Java
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super(“Hello”);
if (person == null) {
throw new NullPointerException(“person is marked @NonNull but is null”);
}
this.name = person.getName();
}
}
Lombok
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super(“Hello”);
this.name = person.getName();
}
}
2. Cleanup
Most of the time when we code, we forget to close resources like Connection, Inputstream, Outstream, etc. Every developer handles it differently. Sometimes, closing a resource on an exception path is missed. To avoid, all these issues Lombok’s Cleanup annotation is very useful.
Close() is considered a cleanup method by default. If cleanup is handled in some other method it can be specified as a parameter.
Vanilla Java
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
}finally {
if (in != null) {
in.close();
}
}
Lombok
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
3. Logger
A code to get a logger instance for the specified logging framework can be generated using a log annotation. Different annotations are provided to get an instance of different logging frameworks like log4j, log4j2, slf4j, etc.
We can also configure custom logger using CustomLog annotation.
Vanilla Java
public class LogExampleOther {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
Lombok
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
4.Builder
This annotation generates a builder for Java bean.
Vanilla Java
public class BuilderExample {
private long created;
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
Set<String> occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
Lombok
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
5. Data
This annotation was introduced to create all the essentials POJO constructs, getters, setters, toString(), hashcode(), equals(), all-arguments-constructor.
Similarly, there are other useful annotations like Synchronized, Value( Immutable class).
Intellij Integration
Install the Lombok plugin to get the APIs that would be generated through the build phase.
For more features please refer official website https://projectlombok.org/features/all
Resources
- https://projectlombok.org/
- https://www.bugdbug.com/post/lombok-remove-boilerplate-code-from-java-source-files
Thanks for reading. If you find this article helpful, share it with a friend.