Lombok’s @Builder annotation is a useful technique to implement the builder pattern that aims to reduce the boilerplate code. In this tutorial, we will learn to apply @Builder to a class and other useful features.

Ensure you have included Lombok in the project and installed Lombok support in the IDE.

If we want to marshal or unmarshal the builder classes with Jackson then we should be using the @Jacksonized annotation.

1. @Builder

The @Builder annotation produces complex builder APIs for the annotated POJO classes.

For example, if we annotate a class Article annotated with @Builder annotation, we can create Article instances using builder API. Internally a class ArticleBuilder and a build() method are auto-generated along with setter like methods for each parameter to the builder class.

import java.util.List;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Builder
@Getter
@ToString
public class Article {
  private Long id;
  private String title;
  private List<String> tags;
}
Article a = Article.builder()
  .id(1L)
  .title("Test Article")
  .tags(Collections.singletonList("Demo"))
  .build();

The @Getter annotation adds accessor methods and @ToString annotation overrides the toString() method printing the values of relevant fields.

2. @Singular Method Support

Using @Singular with @Builder generated singular methods for collection parameters/fields in the auto-generated class. This singular method enables adding a single item to the collection. Note that @Singular annotation supports the Guava collection classes as well.

For example, when we add @Singular to tags field:

@Builder
@Getter
@ToString
public class Article {
  private Long id;
  private String title;

  @Singular
  private List<String> tags;
}

We can achieve the below behavior in the Article class builder.

Article a = Article.builder()
      .id(1L)
      .title("Test Article")
      .tag("TestTag1")
      .tag("TestTag2")
      .build(); 

3. @Builder.Default for Default Values

Sometimes, we want to assign a default value to a field if the client does not populate that field while creating the instance. In such cases, we can use the @Builder.Default on those fields.

@Builder
@Getter
@ToString
public class Article {
  private Long id;

  @Builder.Default
  private String title = "Title Placeholder";

  @Singular
  private List<String> tags;
}

In the above example, if we do not assign the value of title in a new instance, it will be set to “Title Placeholder.”

Article a = Article.builder()
  .id(1L)
  .tag("TestTag1")
  .tag("TestTag2")
  .build();

// Article(id=1, title=Title Placeholder, tags=[TestTag1, TestTag2])
System.out.println(a);  

4. Creating Prototype Instances

A prototype instance is a copy of an existing object rather than creating a new instance from scratch. We can achieve this by enabling the functionality with @Builder(toBuilder = true) and using the toBuilder() method on any existing instance.

@Builder(toBuilder = true)
@Getter
@ToString
public class Article {
    private Long id;

    @Builder.Default
    private String title = "Title Placeholder";

    @Singular
    private List<String> tags;
}

Notice the article title has been changed in the second instance but other fields are the same as the first instance

Article a = Article.builder()
  .id(1L)
  .title("Test Article")
  .tag("Demo")
  .build();

//Article(id=1, title=Test Article, tags=[Demo])
System.out.println(a);

Article.ArticleBuilder nearCopyBuilder = a.toBuilder();
Article b = nearCopyBuilder.title("Final Title").build();

//Article(id=1, title=Final Title, tags=[Demo])
System.out.println(b);

5. Required Parameters to Instance

Our Article class instance does not make sense if it does not have an article id. It should be a mandatory field. For making fields mandatory, we need to add our own ArticleBuilder class with required fields as constructor parameters.

Also, do not forget to add @NonNull annotation if we want to make sure that the client is not passing a null to satisfy the required input validation.

@Builder
@Getter
@ToString
public class Article {
  @NonNull
  private final Long id;
  private String title = "Title Placeholder";
  @Singular
  private final List<String> tags;

  public static ArticleBuilder builder(final Long id) {
    return new ArticleBuilder().id(id);
  }
}
Article a = Article.builder(1L)
  .title("Test Article")
  .tag("Data")
  .build();

//Article(id=1, title=Test Article, tags=[Data])
System.out.println(a);

6. Conclusion

Lombok @Builder annotation is an excellent way of introducing the builder pattern into POJO classes. It supports all kinds of operations and configurations, including marshaling and unmarshalling.

Refer to the official documentation to know the latest features added and any issues.

Happy Learning !!

Sourcecode Download

Was this post helpful?

Join 8000+ Awesome Developers, Like YOU!

Leave a Comment

About HowToDoInJava

This blog provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.