繁体   English   中英

lombok 中的默认值。 如何使用构造函数和构建器初始化默认值

[英]Default value in lombok. How to init default with both constructor and builder

我有一个对象

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

我用两种方式初始化它

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 没有获得默认值。 我将@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

我的猜测是这是不可能的(没有对代码进行 delomboked)。 但是你为什么不实现你需要的构造函数呢? Lombok 旨在让您的生活更轻松,如果 Lombok 无法解决某些问题,请按照老式方法进行操作。

@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

更新
截至 01/2021,此错误似乎已在 Lombok 中修复,至少对于生成的构造函数而言。 请注意,当您混合使用Builder.Default和显式构造函数时,仍然存在类似的问题

由于@Builder.Default注释已损坏,我根本不会使用它。 但是,您可以通过将@Builder注释从类级别移动到自定义构造函数来使用以下方法:

@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仅在一处初始化,从而使代码不易出错isEmailConfirmed易于以后维护
  • UserInfo类将以与使用构建器或无参数构造函数相同的方式初始化

换句话说,该条件成立true

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

在这种情况下,无论您如何创建对象,对象创建都是一致的。 当您的类被映射框架或 JPA 提供者使用时,当您不是由构建器手动实例化它,而是在背后调用无参数构造函数来创建实例时,这一点尤其重要。

该方法上面描述的非常相似,但它有一个很大的缺点。 您必须在两个地方初始化该字段,这会使代码容易出错,因为您需要保持值一致。

另一种方法是定义自己的getter方法来覆盖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;
    }
}

我的经验是@Builder在它是实例化类的唯一方法时效果最好,因此在与@Value而不是@Data配对时效果最好。

对于所有字段以任何顺序可变的类,并且您希望保留链接调用,请考虑将其替换为@Accessors(chain=true)@Accessors(fluent=true)

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

这允许您在代码中流畅地构建对象,并避免不必要地创建 Builder 对象:

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

这是我的方法:

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

在 1.18.2 版本中, @NoArgsConstructor@Builder工作,但不完全。

具有一个或多个字段的构造函数将使所有其他默认初始化为空: new UserInfo("Some nick")将导致isEmailConfirmed再次为 false。

我的处理方法是:

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

这样所有默认字段都将被初始化,我们将获得预期的构造函数。

初始化 No-Arg Constructor的属性

转换
private boolean isEmailConfirmed = true;

public class UserInfo {

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

}

您可以创建一个静态 Builder 类,并填充默认值:

@Data
@Builder(builderClassName="Builder")
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    private int id;
    private String nick;
    private boolean isEmailConfirmed;
    public static class Builder{
          //Set defaults here
          private boolean isEmailConfirmed = true;
    }
}

自定义构造函数和@Builder.Default可能永远不会一起工作。

框架作者希望避免对@Builder进行双重初始化。

我通过public static CLAZZ of(...)方法重用.builder()

@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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM