Standardwert in Lombok. So initialisieren Sie den Standard sowohl mit dem Konstruktor als auch mit dem Builder

Lesezeit: 8 Minuten

Benutzer-Avatar
Vitali

Ich habe ein Objekt

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    private int id;
    private String nick;
    private boolean isEmailConfirmed = true;
}

Und ich initialisiere es auf zwei Arten

UserInfo ui = new UserInfo();
UserInfo ui2 = UserInfo.builder().build();

System.out.println("ui: " + ui.isEmailConfirmed());
System.out.println("ui2: " + ui2.isEmailConfirmed());

Hier ist Ausgabe

ui: true
ui2: false

Es scheint, dass der Builder keinen Standardwert erhält. ich füge hinzu @Builder.Default Anmerkung zu meinem Eigentum und mein Objekt sieht jetzt so aus

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo { 
    private int id;
    private String nick;
    @Builder.Default
    private boolean isEmailConfirmed = true;
}

Hier ist die Konsolenausgabe

ui: false
ui2: true

Wie kann ich sie beide sein lassen true?

Benutzer-Avatar
Michael A. Schaffrath

Meine Vermutung ist, dass es nicht möglich ist (ohne den Code delomboked zu haben). Aber warum implementieren Sie nicht einfach den Konstruktor, den Sie brauchen? Lombok soll Ihnen das Leben erleichtern, und wenn etwas mit Lombok nicht funktioniert, tun Sie es einfach auf die altmodische Weise.

@Data
@Builder
@AllArgsConstructor
public class UserInfo { 
    private int id;
    private String nick;
    @Builder.Default
    private boolean isEmailConfirmed = true;
    
    public UserInfo(){
        isEmailConfirmed = true;
    }
}

Konsolenausgabe:

ui: true
ui2: true

Aktualisieren

Stand 01/2021, Dieser Fehler scheint in Lombok behoben zu sein, zumindest für generierte Konstruktoren. Beachten Sie, dass es immer noch gibt ein ähnliches Problem wenn du mischst Builder.Default und explizite Konstruktoren.

  • Vorsicht! Aktuell dies @Builder.Default ist ab v1.16.16 defekt = Es wird Ihre Felder initialisieren null ungeachtet des angegebenen initialisierten Standardwerts, der auf Feldebene eingestellt ist… Siehe Ausgabe hier

    – DaddyMoe

    28. Februar 2018 um 11:49 Uhr


  • Die Lösung ist nicht perfekt, da Sie das Feld zweimal initialisieren müssen. Einmal bei der Felddeklaration und das zweite Mal in einem Konstruktor. Es macht den Code fehleranfällig. Früher habe ich diesen verwendet: stackoverflow.com/a/50392165/2443502

    – Marcin Klopotek

    17. Mai 2018 um 13:05 Uhr

  • Um den Code weniger fehleranfällig zu machen, habe ich einen JUnit-Test hinzugefügt, der überprüft, ob der No-Arg-Konstruktor gut geschrieben ist: assertThat(new UserInfo().equals(UserInfo.builder().build()).describedAs("some properties marked with @Builder.Default are not properly set in the no-arg constructor").isTrue();

    – Julien Kronegg

    6. Dezember 2018 um 9:37 Uhr


Benutzer-Avatar
Marcin Klopotek

Seit der @Builder.Default Anmerkung ist defekt, ich würde es überhaupt nicht verwenden. Sie können jedoch den folgenden Ansatz verwenden, indem Sie die verschieben @Builder Anmerkung von der Klassenebene zum benutzerdefinierten Konstruktor:

@Data
@NoArgsConstructor
public class UserInfo {

    private int id;
    private String nick;
    private boolean isEmailConfirmed = true;

    @Builder
    @SuppressWarnings("unused")
    private UserInfo(int id, String nick, Boolean isEmailConfirmed) {
        this.id = id;
        this.nick = nick;
        this.isEmailConfirmed = Optional.ofNullable(isEmailConfirmed).orElse(this.isEmailConfirmed);
    }
}

So stellen Sie sicher:

  • das Feld isEmailConfirmed wird nur an einer Stelle initialisiert, wodurch der Code weniger fehleranfällig und später einfacher zu warten ist
  • das UserInfo Die Klasse wird auf die gleiche Weise initialisiert, entweder Sie verwenden einen Builder oder einen No-Args-Konstruktor

Mit anderen Worten, die Bedingung gilt true:

new UserInfo().equals(UserInfo.builder().build())

In diesem Fall ist die Objekterstellung unabhängig davon, wie Sie sie erstellen, konsistent. Dies ist besonders wichtig, wenn Ihre Klasse von einem Mapping-Framework oder einem JPA-Provider verwendet wird, wenn Sie sie nicht manuell von einem Builder instanziieren, sondern hinter Ihrem Rücken ein No-Args-Konstruktor aufgerufen wird, um die Instanz zu erstellen.

Der oben beschriebene Ansatz ist sehr ähnlich, hat aber einen großen Nachteil. Sie müssen das Feld an zwei Stellen initialisieren, was den Code fehleranfällig macht, da Sie die Werte konsistent halten müssen.

  • Da Lombok viele Macken hat, die, wenn Sie sich dessen nicht bewusst sind, viele Kopfschmerzen als Vorteile bringen können, können Sie auch entweder Immutables (immutables.github.io) oder Autowert (github.com/google/auto/blob/master/value/userguide/builders.md).

    – Marcin Klopotek

    17. Mai 2018 um 13:21 Uhr


  • Alle anderen Tools haben einen einzigen großen Nachteil: Sie generieren eine neue Klasse. Sie haben sicherlich weniger Probleme, da sie nur Anmerkungsprozessoren sind, die eine bekannte (und hässliche) API verwenden. Lombok muss anders funktionieren und hat tatsächlich ein paar Bugs. Ich benutze es jedoch seit seinen Anfängen und bin ziemlich zufrieden damit.

    – maaartinus

    5. November 2018 um 17:32 Uhr

  • Die Anmerkung ist nicht vollständig defekt, hat aber Probleme mit nicht primitiven Objekten. Die Voreinstellungen funktionieren bei mir mit Boolean ab Version 1.16.22

    – Simonturm

    10. Januar 2019 um 21:11 Uhr

  • Für alle, die wissen wollen, wie man das macht, wenn man es hat @Singular Anmerkungen, sehen Sie sich diesen Kommentar an: github.com/rzwitserloot/lombok/issues/… TLDR: Bewegen Sie sich @Singular Anmerkungen in den neu erstellten privaten Konstruktor.

    – Kibowki

    18. Januar 2019 um 5:12 Uhr

  • Mit der neusten Version von Lombok scheint es nicht mehr kaputt zu gehen.

    – David Konrad

    10. März 2020 um 17:48 Uhr

Eine andere Möglichkeit besteht darin, Ihre eigenen zu definieren Getter Methode überschreiben das lombok Getter:

@Data
@Builder
@AllArgsConstructor
public class UserInfo { 
    private int id;
    private String nick;
    private Boolean isEmailConfirmed;

    public Boolean getIsEmailConfirmed(){
      return Objects.isNull(isEmailConfirmed) ? true : isEmailConfirmed;
    }
}

  • Es wird davon abgeraten, eine Logik in eigenschaftsbasierten Gettern zu haben. Stellen Sie sich die Situation vor, Sie möchten eine zusätzliche Methode in der Klasse hinzufügen, die Methode hängt vom Feld ab (isEmailConfirmed). Es kann für den Implementierer verwirrend sein, ein Feld direkt zu verwenden oder stattdessen Getter aufzurufen. Die Werte können abweichen, was zu Fehlern führen kann.

    – Marcin Klopotek

    18. Oktober 2018 um 10:58 Uhr


  • @MarcinKłopotek Ihr Punkt ist gut, aber ich denke, dass dies durch die Verwendung einer veralteten Anmerkung in diesem privaten Feld gehandhabt werden kann, um die direkte Verwendung einzuschränken. Wenn ich der Implementierer dieser Klasse bin und weiß, dass ich einen Standardwert benötige und einen Code dafür geschrieben habe, sollte ich natürlich wissen, dass ich dieses Feld nicht direkt verwenden sollte, um es für eine andere Logikmethode darin zu verwenden Klasse.

    – Sahil Chhabra

    19. Oktober 2018 um 2:49 Uhr


  • @SahilChhabra Mehrdeutigkeit ist schlecht. Deprecated hat einen besonderen Zweck, es sei denn, Sie möchten die Benutzer Ihres Codes weiter verwirren. zuletzt vorausgesetzt dass es “Ihre Klasse” und “Sie sollten wissen” ist, dass dieses Feld nicht verwendet werden darf usw. usw. hat ein weiteres ernstes Problem. Halten Sie sich an die Konsistenz und Ihr Code wird sowohl zukünftigen Entwicklern als auch seinen Endbenutzern Sorgfalt entgegenbringen!

    – prash

    26. Juli 2019 um 14:46 Uhr

  • @prash Sie können nicht immer alle Best Practices befolgen. Manchmal müssen Sie abweichen, wenn Sie einen fehlerhaften Drittanbietercode verwenden. Sie können sich dafür entscheiden, den Code von Drittanbietern überhaupt nicht zu verwenden, oder eine Problemumgehung wählen, indem Sie sicherstellen, dass zukünftige Entwickler über die Verwendung informiert werden (hier kann Deprecated diese Aufgabe gut erledigen). Es ist nur eine Wahl und Meinung. Ich freue mich, dass du eine eigene Meinung hast. Außerdem empfehle ich Ihnen, sich erneut über die veraltete Verwendung zu informieren – docs.oracle.com/javase/7/docs/technotes/guides/javadoc/…

    – Sahil Chhabra

    26. Juli 2019 um 18:02 Uhr

  • @prash Ich stimme zu, dass seine Antwort besser ist. Ich habe nur eine andere Möglichkeit gegeben, es zu tun. Ich stimme zu, dass es Probleme gibt, wie Sie sie erwähnt haben, aber das hängt vom Anwendungsfall ab. Außerdem habe ich nie etwas gegen Best Practices gesagt. Ich habe nur gesagt, dass Sie manchmal nicht jeder Best Practice folgen können (wieder abhängig vom Anwendungsfall).

    – Sahil Chhabra

    29. Juli 2019 um 6:19 Uhr


Meine Erfahrung ist das @Builder funktioniert am besten, wenn es das einzige Mittel zum Instanziieren einer Klasse ist, und funktioniert daher am besten, wenn es mit gepaart wird @Value statt @Data.

Für Klassen, in denen alle Felder sowieso in beliebiger Reihenfolge veränderbar sind und für die Sie die verketteten Aufrufe beibehalten möchten, sollten Sie erwägen, sie durch zu ersetzen @Accessors(chain=true) oder @Accessors(fluent=true).

@Data
@Accessors(fluent=true)
public class UserInfo {
    private int id;
    private String nick;
    private boolean isEmailConfirmed = true;
}

Auf diese Weise können Sie Ihre Objekte fließend im Code erstellen und unnötige Erstellung von Builder-Objekten vermeiden:

UserInfo ui = new UserInfo().id(25).nick("John");

Hier ist mein Ansatz:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class UserInfo { 
    private int id;
    private String nick;
    private boolean isEmailConfirmed = true;
}

Und dann

UserInfo ui = new UserInfo().toBuilder().build();

  • Ich verwende den gleichen Ansatz, aber anstatt zu verwenden: UserInfo ui = new UserInfo().toBuilder().build(); Ich verwende am liebsten: UserInfo ui = new UserInfo(); dasselbe mit weniger Code.

    – Ali Ertugrul

    21. Juli 2020 um 7:10 Uhr


Benutzer-Avatar
Andrej Serebrjanskij

In Version 1.18.2 beides @NoArgsConstructor und @Builder funktionieren, aber nicht vollständig.

Konstruktoren mit einem oder mehreren Feldern nullen alle anderen Standardinitialisierungen: new UserInfo("Some nick") wird verursachen isEmailConfirmed wieder falsch sein.

Mein Weg, damit umzugehen, ist:

public UserInfo(String nick) {
  this();
  this.nick = nick;
}

Auf diese Weise werden alle Standardfelder initialisiert und wir erhalten den erwarteten Konstruktor.

  • Ich verwende den gleichen Ansatz, aber anstatt zu verwenden: UserInfo ui = new UserInfo().toBuilder().build(); Ich verwende am liebsten: UserInfo ui = new UserInfo(); dasselbe mit weniger Code.

    – Ali Ertugrul

    21. Juli 2020 um 7:10 Uhr


Benutzer-Avatar
ifelse.codes

Initialisieren Sie die Eigenschaften im No-Arg Constructor

umgewandelt
private boolean isEmailConfirmed = true;

zu

public class UserInfo {

    public UserInfo() {
        this.isEmailConfirmed = true;
    }

}

1228520cookie-checkStandardwert in Lombok. So initialisieren Sie den Standard sowohl mit dem Konstruktor als auch mit dem Builder

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy