Lombok @builder в классе, который расширяет другой класс

У меня есть два класса Child extends Parent. Мне нужно поставить аннотацию @Builder на классы, чтобы мне не нужно было создавать свой конструктор.

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

import lombok.AllArgsConstructor;
import lombok.Builder;

@AllArgsConstructor([email protected]__(@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;
}

Мне нужно создать дочерний экземпляр таким образом, чтобы

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

Но до сих пор я получаю ошибки, упомянутые в этом комментарии кодов. Может ли кто-нибудь указать мне в правильном направлении, как достичь этого с помощью ломбока или любой другой подобной библиотеки?

Суб-вопрос

Почему компиляция @AllArgsConstructor([email protected]__(@Autowired)), но @AllArgsConstructor([email protected]__(@Builder)) не работает?

Ответ 1

Смотрите https://blog.codecentric.de/en/2016/05/reducing-boilerplate-code-project-lombok/ (часть @Builder и наследование)

Скорректировано для ваших классов

@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;
  }
}

С помощью этой настройки

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

работает правильно

Ответ 2

Начиная с версии 1.18.2, ломбок включает в себя новый эксперимент @SuperBuilder. Он поддерживает поля из суперклассов (также абстрактных). С его помощью решение так просто:

@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();

Обновление 2019-10-09: если вы используете IntelliJ, для использования @SuperBuilder вам нужна как минимум версия 0.27 плагина IntelliJ Lombok.

PS: @AllArgsConstructor([email protected]__(@Builder)) не работает, потому что @Builder является аннотацией обработки аннотации, которую lombok преобразует в код во время компиляции. Создание и последующее преобразование новой аннотации lombok потребует нескольких итераций обработки аннотации, и lombok не поддерживает это. @Autowired, напротив, является обычной аннотацией Java, доступной во время выполнения.

Ответ 3

У меня есть аналогичный, но немного другой вариант использования. В моем случае у меня есть цепочка абстрактных суперклассов, которые строят и выполняют запросы. Различные запросы имеют общие параметры, но не каждый запрос поддерживает все параметры. Строители для конкретных запросов должны предоставлять только методы для установки поддерживаемых параметров для данного запроса.

Я начал работу с реализацией конструктора на основе конструктора, но это привело к слишком большому шаблону кода, особенно если у вас есть много полей для настройки. Теперь я придумал следующие решения, которые выглядят намного чище для меня.

В основном конкретные подклассы определяют фактические поля, которые Builder должен поддерживать, а аннотация Getter приводит к переопределению соответствующих методов суперкласса. Геттеры в абстрактных суперклассах могут даже быть определены как абстрактные, чтобы требовать конкретных реализаций для определения этих полей.

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();
    }
}