Lombok @builder on a class that extends another class

JavaSpringHibernateLombok

Java Problem Overview


I have two classes Child extends Parent. I need to put @Builder annotation on the classes such that I do not need to create the builder my self.

package jerry;// Internal compiler error: java.lang.NullPointerException

import lombok.AllArgsConstructor;
import lombok.Builder;

@AllArgsConstructor(onConstructor=@__(@Builder))
public class Child extends Parent { 
//Multiple markers at this line
//	- Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor
//	- overrides java.lang.Object.toString
    
   private String a;
   private int b;
   private boolean c;
   
}


@Builder
public class Parent {
    private double d;
    private float e;
}

I need to be able to build child instance such that

Child child = Child.builder().a("aVal").b(1000).c(true).d(10.1).e(20.0F).build();

But so far I am getting the errors mentioned in side that code comments. Can any one point me to the right direction how to achieve it with lombok or any other similar library ?

Sub-question

Why does @AllArgsConstructor(onConstructor=@__(@Autowired)) compile but @AllArgsConstructor(onConstructor=@__(@Builder)) does not?

Java Solutions


Solution 1 - Java

Since release 1.18.2 lombok includes the new experimental @SuperBuilder. It supports fields from superclasses (also abstract ones). With it, the solution is as simple as this:

@SuperBuilder
public class Child extends Parent {
   private String a;
   private int b;
   private boolean c;
}

@SuperBuilder
public class Parent {
    private double d;
    private float e;
}

Child instance = Child.builder().b(7).e(6.3).build();

Update 2019-10-09: If you use IntelliJ, you need at least version 0.27 of the IntelliJ Lombok plugin to use @SuperBuilder.

PS: @AllArgsConstructor(onConstructor=@__(@Builder)) does not work because @Builder is an annotation-processing annotation that lombok translates to code during compilation. Generating and then translating new lombok annotation would require several iterations of annotation processing, and lombok does not support that. @Autowired, in contrast, is a regular Java annotation available at runtime.

Solution 2 - Java

See in https://blog.codecentric.de/en/2016/05/reducing-boilerplate-code-project-lombok/ (@Builder and inheritance part)

Adjusted to your classes

@AllArgsConstructor
public class Parent {
  private double d;
  private float e;
}

public class Child extends Parent {
  private String a;
  private int b;
  private boolean c;

  @Builder
  public Child(String a, int b, boolean c, double d, float e) {
    super(d, e);
    this.a = a;
    this.b = b;
    this.c = c;
  }
}

With this setup

Child child = Child.builder().a("aVal").b(1000).c(true).d(10.1).e(20.0F).build();

works correctly

Solution 3 - Java

You need to use @SuperBuilder(toBuilder = true) in every object.

@Data
@SuperBuilder(toBuilder = true)
public class Parent extends Child {
}

@Data
@SuperBuilder(toBuilder = true)
public class Child {
}

Solution 4 - Java

I have a similar but slightly different use case. In my case, I have a chain of abstract super classes that build and execute requests. Different requests share some common parameters, but not every request supports all parameters. The builders for concrete requests should only provide methods for setting supported parameters for a given request.

I started out with the constructor-based builder implementations, but this resulted in too much boilerplate code, especially if you have many fields to be configured. I have now come up with the following solutions, which looks much cleaner to me.

Basically the concrete subclasses define the actual fields that the Builder should support, and the Getter annotation results in the corresponding superclass methods to be overridden. The getters in the abstract superclasses can even be defined as abstract to require concrete implementations to define those fields.

public abstract class AbstractSuperClass1 {
    protected String getParamA() { return "defaultValueA"; }

    public final void doSomething() {
	    System.out.println(getParamA());
	    doSomeThingElse();
    }

    protected abstract void doSomeThingElse();
}

public abstract class AbstractSuperClass2 extends AbstractSuperClass1 {
    protected String getParamB() { return "defaultValueB"; }

    protected void doSomeThingElse() {
	    System.out.println(getParamB());
    }
}

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;

@Getter(AccessLevel.PROTECTED) @Builder
public class ConcreteClass1 extends AbstractSuperClass2 {
    private final String paramA;
    // Not supported by this implementation: private final String paramB;

    public static void main(String[] args) {
	    ConcreteClass1.builder()
           .paramA("NonDefaultValueA")
           .build().doSomething();
    }
}

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;

@Getter(AccessLevel.PROTECTED) @Builder
public class ConcreteClass2 extends AbstractSuperClass2 {
    private final String paramA;
    private final String paramB;

    public static void main(String[] args) {
    	ConcreteClass2.builder()
            .paramA("NonDefaultValueA").paramB("NonDefaultValueB")
            .build().doSomething();
    }
}

Solution 5 - Java

if your child class wants to have a builder method when the parent already have one,then you can create an all args constructor for the child class and then annotate builder on the constructor of child class and set the property

> @Builder(builderMethodName = "custombuildername")

Then invoke the customname builder while creating the child class Object.

package jerry;

import lombok.AllArgsConstructor;
import lombok.Builder;

@AllArgsConstructor(onConstructor=@__(@Builder))
public class Child extends Parent { 

   private String a;
   private int b;
   private boolean c;

   @Builder(builderMethodName = "childBuilder")
   public Child(String a, int b, boolean c, double d, float e){
       super(d,e);
   }

}


@Builder
public class Parent {
    private double d;
    private float e;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzurView Question on Stackoverflow
Solution 1 - JavaJan RiekeView Answer on Stackoverflow
Solution 2 - JavahammerfestView Answer on Stackoverflow
Solution 3 - JavaTomaszView Answer on Stackoverflow
Solution 4 - JavarsendenView Answer on Stackoverflow
Solution 5 - JavaglegshotView Answer on Stackoverflow