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

Java lombok中的默认值。如何使用构造函数和生成器初始化默认值,java,lombok,Java,Lombok,我有一个目标 @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

我有一个目标

@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.Default
注释添加到我的属性中,我的对象现在如下所示

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo { 
    private int id;
    private String nick;
    @Builder.Default
    private boolean isEmailConfirmed = true;
}
这里是控制台输出

ui: false
ui2: true

我怎样才能使它们都成为
真的

我猜这是不可能的(如果没有删除代码的话)。但是为什么不实现所需的构造函数呢?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
更新

自2021年1月起,至少针对生成的构造函数。请注意,当您混合使用
Builder.Default
和显式构造函数时,仍然存在一些问题。

另一种方法是定义您自己的getter方法覆盖lombokgetter:

@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
注释从类级别移动到自定义构造函数来使用以下方法:

@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);
    }
}
通过这种方式,您可以确保:

  • 字段
    isemailcomfixed
    仅在一个位置初始化,使代码更不容易出错,更易于以后维护
  • UserInfo
    类的初始化方式与使用生成器或无参数构造函数的方式相同
换句话说,该条件保持
true

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

这种方法非常相似,但有一个主要缺点。您必须在两个地方初始化字段,这使得代码容易出错,因为您需要保持值一致。

以下是我的方法:

@数据
@诺尔格构装师
@AllArgsConstructor
@生成器(toBuilder=true)
公共类用户信息{
私有int-id;
私人字符串尼克;
私有布尔值isEmailConfirmed=true;
}
然后

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

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

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

我通过(…)
方法的
公共静态分类重用
.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();
    }
}

查看相应的讨论:

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

对于所有字段都以任意顺序可变的类,并希望保持链式调用,请考虑用<代码> @访问器(链=true)<代码> >或代码> @访问器(FLUENT=TRUE)< /COD> < /P>

@数据
@访问器(fluent=true)
公共类用户信息{
私有int-id;
私人字符串尼克;
私有布尔值isEmailConfirmed=true;
}
这允许您在代码中流畅地构造对象,并避免不必要地创建生成器对象:

UserInfo ui=newuserinfo().id(25).nick(“John”);

初始化无参数
构造函数中的属性

已转换
private boolean isemailconfirm=true


在1.18.2版中,
@noargsconstuctor
@Builder
都可以工作,但不能完全工作

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

我的处理方法是:

public UserInfo(字符串){
这个();
this.nick=nick;
}

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

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

@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
已从v1.16.16中断开=它会将字段初始化为
null
,而不考虑在字段级别设置的指定默认初始化值。。。请看,解决方案并不完美,因为您有两次初始化字段。一次在字段声明中,第二次在构造函数中。这使得代码容易出错。我曾经使用这个函数来减少代码的错误倾向,我添加了一个JUnit测试来验证无参数构造函数是否编写良好:
assertThat(new UserInfo().equals(UserInfo.builder().build()).descripbedas(“在无参数构造函数中未正确设置标记为@builder.Default的某些属性”).isTrue()由于Lombok有许多怪癖,当你不知道这些怪癖可能带来很多麻烦而不是好处时,你也可以尝试Immutables()或Autovalue()。所有其他工具都有一个很大的缺点:它们生成一个新类。它们的问题肯定较少,因为它们只是使用著名(而且丑陋)的注释处理器API.Lombok必须以不同的方式工作,它确实有一些bug。但是,我从一开始就在使用它,而且我对它非常满意。注释并没有完全损坏,但它在非基本对象方面存在问题。对于任何想知道如何使用它的人来说,默认值适用于
Boolean
版本的
1.16.22
如果您有
@Singular
注释,请查看以下注释:TLDR:将您的
@Singular
注释移动到新创建的私有构造函数中。最新版本的Lombok似乎不再破坏它。不鼓励在基于属性的getter中使用逻辑。想象一下您想要的情况增加一个额外的方法
@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;
    }
}