简体   繁体   English

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

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

I have an object我有一个对象

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

And I initialize it in two ways我用两种方式初始化它

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

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

Here is output这是输出

ui: true
ui2: false

It seems that builder does not get a default value.似乎 builder 没有获得默认值。 I add @Builder.Default annotation to my property and my object now looks like this我将@Builder.Default注释添加到我的属性中,我的对象现在看起来像这样

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

Here is console output这是控制台输出

ui: false
ui2: true

How can I make them both be true ?我怎样才能让它们都是true

My guess is that it's not possible (without having delomboked the code).我的猜测是这是不可能的(没有对代码进行 delomboked)。 But why don't you just implement the constructor you need?但是你为什么不实现你需要的构造函数呢? Lombok is meant to make your life easier, and if something won't work with Lombok, just do it the old fashioned way. Lombok 旨在让您的生活更轻松,如果 Lombok 无法解决某些问题,请按照老式方法进行操作。

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

Console output:控制台输出:

ui: true
ui2: true

Update更新
As of 01/2021, this bug seems to be fixed in Lombok , at least for generated constructors.截至 01/2021,此错误似乎已在 Lombok 中修复,至少对于生成的构造函数而言。 Note that there is still a similar issue when you mix Builder.Default and explicit constructors.请注意,当您混合使用Builder.Default和显式构造函数时,仍然存在类似的问题

Since the @Builder.Default annotation is broken , I wouldn't use it at all.由于@Builder.Default注释已损坏,我根本不会使用它。 You can, however, use the following approach by moving the @Builder annotation from class level to the custom constructor:但是,您可以通过将@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);
    }
}

This way you ensure:这样您可以确保:

  • the field isEmailConfirmed is initialized only in one place making the code less error-prone and easier to maintain later字段isEmailConfirmed仅在一处初始化,从而使代码不易出错isEmailConfirmed易于以后维护
  • the UserInfo class will be initialized the same way either you use a builder or a no-args constructor UserInfo类将以与使用构建器或无参数构造函数相同的方式初始化

In other words, the condition holds true :换句话说,该条件成立true

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

In that case, the object creation is consistent no matter how you create it.在这种情况下,无论您如何创建对象,对象创建都是一致的。 It is especially important when your class is used by a mapping framework or by JPA provider when you are not instantiating it manually by a builder but a no-args constructor is invoked behind your back to create the instance.当您的类被映射框架或 JPA 提供者使用时,当您不是由构建器手动实例化它,而是在背后调用无参数构造函数来创建实例时,这一点尤其重要。

The approach described above is very similar but it has a major drawback.该方法上面描述的非常相似,但它有一个很大的缺点。 You have to initialize the field in two places which makes the code error-prone as you are required to keep the values consistent.您必须在两个地方初始化该字段,这会使代码容易出错,因为您需要保持值一致。

Another way is define your own getter method overriding the lombok getter:另一种方法是定义自己的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;
    }
}

My experience is that @Builder works best when it is the only means of instantiating a class, and therefore works best when paired with @Value rather than @Data .我的经验是@Builder在它是实例化类的唯一方法时效果最好,因此在与@Value而不是@Data配对时效果最好。

For classes where all fields are mutable in any order anyway, and for which you want to keep the chained calls, consider replacing it with @Accessors(chain=true) or @Accessors(fluent=true) .对于所有字段以任何顺序可变的类,并且您希望保留链接调用,请考虑将其替换为@Accessors(chain=true)@Accessors(fluent=true)

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

This allows you to construct your objects fluently in the code, and avoid un-necessary creation of Builder objects:这允许您在代码中流畅地构建对象,并避免不必要地创建 Builder 对象:

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

Here's my approach :这是我的方法:

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

And then接着

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

In version 1.18.2 both @NoArgsConstructor and @Builder work, but not completely.在 1.18.2 版本中, @NoArgsConstructor@Builder工作,但不完全。

Constructor with one or more fields will null all other default initialisations: new UserInfo("Some nick") will cause isEmailConfirmed to be false again.具有一个或多个字段的构造函数将使所有其他默认初始化为空: new UserInfo("Some nick")将导致isEmailConfirmed再次为 false。

My way to handle this is:我的处理方法是:

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

This way all default fields will be initialised and we'll get expected constructor.这样所有默认字段都将被初始化,我们将获得预期的构造函数。

Initialize the properties in the No-Arg Constructor初始化 No-Arg Constructor的属性

converted转换
private boolean isEmailConfirmed = true;

to

public class UserInfo {

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

}

You can create a static Builder class with default values populated:您可以创建一个静态 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;
    }
}

Custom constructors and @Builder.Default probably will never work together.自定义构造函数和@Builder.Default可能永远不会一起工作。

Framework authors want to avoid double initializations for @Builder .框架作者希望避免对@Builder进行双重初始化。

I reuse .builder() by public static CLAZZ of(...) methods:我通过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();
    }
}

Check corresponding discussion: https://github.com/rzwitserloot/lombok/issues/1347查看对应讨论: https : //github.com/rzwitserloot/lombok/issues/1347

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

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