1. Introduction
Project Lombok is a popular Java library to help reduce the amount of boilerplate code a developer needs to write.
In this tutorial, we’ll take a look at how Lombok’s @Builder annotation works and how we can customize it for our specific needs.
2. Maven Dependency
Let’s start by adding the dependency to our pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
3. Lombok Builder Annotation
Before we look into customizing Lombok’s generated builder class, let’s do a quick recap of how the Lombok @Builder annotation works. We already have a full introduction into Lombok’s features.
The @Builder annotation can be used to automatically generate a builder for our class. For our example, we’ll use a messaging system where one user can send a message to another user. The message is either a simple text string or a File. Using Lombok, we can define our Message class as follows:
@Builder
@Data
public class Message {
private String sender;
private String recipient;
private String text;
private File file;
}
@Data generates all the boilerplate that is normally associated with a simple POJO (Plain Old Java Object): getters for all fields, setters for all non-final fields, and appropriate toString, equals and hashCode implementations, and a constructor.
Using the generated builder, we can now generate instances of our Message class:
Message message = Message.builder()
.sender("[email protected]")
.recipient("[email protected]")
.text("How are you today?")
.build();
The @Builder annotation also supports default values for attributes but we won’t go into that now. It should be clear from this example that the @Builder annotation is quite powerful and can replace a lot of boilerplate code.
4. Customizing Lombok Builders
The previous section showed how we can use Lombok to generate a builder class. But there may be cases where the generated builder is not enough. In our example, we have a constraint that the message can only contain either text or a file. It cannot have both. Lombok doesn’t know that of course and the generated builder will happily allow us to get into that illegal state.
Luckily we can address this problem customizing the builder.
Customizing a Lombok builder is simple and straightforward: we write the parts of the builder that we want to customize and the Lombok @Builder annotation will simply not generate those parts. So in our example, that would be:
public static class MessageBuilder {
private String text;
private File file;
public MessageBuilder text(String text) {
this.text = text;
verifyTextOrFile();
return this;
}
public MessageBuilder file(File file) {
this.file = file;
verifyTextOrFile();
return this;
}
private void verifyTextOrFile() {
if (text != null && file != null) {
throw new IllegalStateException("Cannot send 'text' and 'file'.");
}
}
}
Please note that we did not have to declare the sender and recipient members, or the builder methods associated with them. Lombok will still generate those for us.
If we try to generate a Message instance with both text and a file with the following code:
Message message = Message.builder()
.sender("[email protected]")
.recipient("[email protected]")
.text("How are you today?")
.file(new File("/path/to/file"))
.build();
It will result in the following exception:
Exception in thread "main" java.lang.IllegalStateException: Cannot send 'text' and 'file'.
5. Conclusion
In this quick article, we looked at how to customize the Lombok builder.
As always, the code is available over on GitHub.