I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

The Lombok library provides a great way to implement the Builder Pattern without writing any boilerplate code: the @Builder annotation.

In this short tutorial, we’re specifically going to learn how to deal with the @Builder annotation when inheritance is involved.

For a wider overview of the annotation, we can refer to Using Lombok’s @Builder Annotation.

A detailed look to the Project Lombok library is also available in Introduction to Project Lombok.

2. Lombok @Builder and Inheritance

2.1. Defining the Problem

Let’s suppose our Child class extends a Parent class:

@Getter
@AllArgsConstructor
public class Parent {
    private final String parentName;
    private final int parentAge;
}

@Getter
@Builder
public class Child extends Parent {
    private final String childName;
    private final int childAge;
}

When using @Builder on a class which extends another class like that, we’ll get the following compilation error on the annotation:

Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor

This is due to the fact that Lombok doesn’t take into account the fields of the superclasses, but only the ones from the current class.

2.2. Solving the Problem

Luckily for us, there’s a simple workaround. We can generate (with our IDE or even manually) a field-based constructor, which includes also the fields from the superclasses, and annotate it with @Builder, instead of the class:

@Getter
@AllArgsConstructor
public class Parent {
    private final String parentName;
    private final int parentAge;
}

@Getter
public class Child extends Parent {
    private final String childName;
    private final int childAge;

    @Builder
    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }
}

This way, we’ll be able to access a convenient builder from the Child class, which will allow us to specify also the Parent class’ fields:

Child child = Child.builder()
  .parentName("Andrea")
  .parentAge(38)
  .childName("Emma")
  .childAge(6)
  .build();

assertThat(child.getParentName()).isEqualTo("Andrea");
assertThat(child.getParentAge()).isEqualTo(38);
assertThat(child.getChildName()).isEqualTo("Emma");
assertThat(child.getChildAge()).isEqualTo(6);

2.3. Making Multiple @Builders Coexist

In case the superclass is itself annotated with @Builder, we’ll get the following error when annotating the Child class’ constructor:

The return type is incompatible with Parent.builder()

This is because the Child class is trying to expose both the Builders with the same name.

We can fix this problem by assigning a unique name to at least one of the builder methods:

@Getter
@Builder
public class Parent {
    private final String parentName;
    private final int parentAge;
}

@Getter
public class Child extends Parent {
    private final String childName;
    private final int childAge;
    
    @Builder(builderMethodName = "childBuilder")
    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }
}

We’ll then be able to obtain a ParentBuilder through Child.builder() and a ChildBuilder through Child.childBuilder().

3. Conclusion

We’ve seen how to deal with the common pitfalls of using the @Builder annotation in classes making use of inheritance.

As always, the full source code is available over on Github.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS