web-dev-qa-db-ja.com

ロンボクのデフォルト値。コンストラクターとビルダーの両方でデフォルトを初期化する方法

オブジェクトがあります

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

そして、私は2つの方法でそれを初期化します

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

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

ここに出力されます

ui: true
ui2: false

ビルダーはデフォルト値を取得していないようです。プロパティに@Builder.Default注釈を追加すると、オブジェクトは次のようになります

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

コンソール出力です

ui: false
ui2: true

両方をtrueにするにはどうすればよいですか?

35
Vitalii

私の推測では、それは不可能です(コードを削除していなければ)。しかし、なぜ必要なコンストラクタを実装しないのですか?ロンボクはあなたの生活を楽にすることを目的としており、何かがロンボクで動作しない場合は、昔ながらの方法でそれを行ってください。

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

    public UserInfo(){
        isEmailConfirmed = true;
    }
}

コンソール出力:

ui: true
ui2: true
18

@Builder.Defaultアノテーションが壊れている なので、まったく使用しません。ただし、次のアプローチを使用できます。

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

これにより、次のことが保証されます。

  • フィールドisEmailConfirmedは1か所でのみ初期化されるため、コードのエラーが発生しにくく、後で保守しやすくなります。
  • UserInfoクラスは、ビルダーまたは引数なしのコンストラクターを使用する場合と同じ方法で初期化されます

つまり、条件はtrueを保持します。

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

その場合、オブジェクトの作成は、作成方法に関係なく一貫しています。クラスをマッピングフレームワークまたはJPAプロバイダーで使用する場合、ビルダーで手動でインスタンス化するのではなく、背後で引数なしのコンストラクターを呼び出してインスタンスを作成するときに特に重要です。

アプローチ 上記 は非常に似ていますが、大きな欠点があります。 2つの場所でフィールドを初期化する必要があります。これにより、値の一貫性を保つ必要があるため、コードエラーが発生しやすくなります。

31

別の方法は、独自のgetter method overrideinglombok 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;
    }
}
2
Sahil Chhabra

私のアプローチは次のとおりです。

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

その後

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

カスタムコンストラクターと@Builder.Defaultは、おそらく一緒には機能しません。

フレームワークの作成者は、@Builderの二重の初期化を避けたいと考えています。

.builder()メソッドでpublic static CLAZZ of(...)を再利用します:

@Builder
public class Connection {
    private String user;
    private String pass;

    @Builder.Default
    private long timeout = 10_000;

    @Builder.Default
    private String port = "8080";

    public static Connection of(String user, String pass) {
        return Connection.builder()
            .user(user)
            .pass(pass)
            .build();
    }

    public static Connection of(String user, String pass, String port) {
        return Connection.builder()
            .user(user)
            .pass(pass)
            .port(port)
            .build();
    }

    public static Connection of(String user, String pass, String port, long timeout) {
        return Connection.builder()
            .user(user)
            .pass(pass)
            .port(port)
            .timeout(timeout)
            .build();
    }
}

対応する議論を確認してください: https://github.com/rzwitserloot/lombok/issues/1347

0
gavenkoa