web-dev-qa-db-de.com

Lombok @builder für eine Klasse, die eine andere Klasse erweitert

Ich habe zwei Klassen Child erweitert Parent. Ich muss @Builder-Annotation in die Klassen einfügen, sodass ich den Builder nicht selbst erstellen muss.

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

Ich muss in der Lage sein, eine Kindinstanz so aufzubauen 

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

Aber bis jetzt bekomme ich die Fehler, die in den Code-Kommentaren erwähnt werden. Kann mir jemand die richtige Richtung zeigen, wie ich es mit Lombok oder einer ähnlichen Bibliothek erreichen kann?

Unterfrage

Warum wird @AllArgsConstructor([email protected]__(@Autowired)) kompiliert, @AllArgsConstructor([email protected]__(@Builder)) jedoch nicht?

18
zur

Siehe in https://blog.codecentric.de/de/2016/05/reducer-boilerplate-code-project-lombok/ (@Builder und Vererbung Teil)

An Ihre Klassen angepasst

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

Mit diesem Setup

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

funktioniert richtig

23
hammerfest

Die neueste Lombok-Version 1.18.2 enthält den neuen experimentellen @SuperBuilder . Es unterstützt Felder von Superklassen (auch abstrakte). Damit ist die Lösung so einfach:

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

PS: @AllArgsConstructor([email protected]__(@Builder)) funktioniert nicht, da @Builder eine Annotationsverarbeitungs-Annotation ist, die Lombok während der Kompilierung in Code übersetzt. Die Erstellung und anschließende Übersetzung neuer Lombok-Annotationen würde mehrere Iterationen der Annotationsverarbeitung erfordern, und Lombok unterstützt dies nicht. Im Gegensatz dazu ist @Autowired eine normale Java-Anmerkung, die zur Laufzeit verfügbar ist.

17
Jan Rieke

Ich habe einen ähnlichen, aber etwas anderen Anwendungsfall. In meinem Fall habe ich eine Kette abstrakter Superklassen, die Anforderungen erstellen und ausführen. Unterschiedliche Anforderungen haben einige gemeinsame Parameter, aber nicht jede Anforderung unterstützt alle Parameter. Die Builder für konkrete Anforderungen sollten nur Methoden zum Festlegen unterstützter Parameter für eine bestimmte Anforderung bereitstellen. 

Ich habe mit den Konstruktor-basierten Builder-Implementierungen angefangen, was jedoch zu viel Boilerplate-Code führte, insbesondere wenn Sie viele Felder konfigurieren müssen. Ich habe jetzt folgende Lösungen gefunden, die für mich viel sauberer erscheinen.

Grundsätzlich definieren die konkreten Unterklassen die tatsächlichen Felder, die der Generator unterstützen soll, und die Getter-Annotation führt dazu, dass die entsprechenden Superklassenmethoden überschrieben werden. Die Getter in den abstrakten Superklassen können sogar als abstrakt definiert werden, um konkrete Implementierungen zum Definieren dieser Felder zu erfordern.

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