在方法级别使用 Lombok @Builder 时提供默认值?

Provide default values when using Lombok @Builder on method level?

假设有以下示例性 class 不受我的控制,即我无法更改其行为。

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class RegistrationRequest {

    private String email;
    private String emailRepeat;
    private String password;
    private String passwordRepeat;
}

我想使用 Lombok @Builder 为这个 class 构建测试数据,所以我将 @Builder 注释放在方法级别,如下所示:

public class SomeTestUtils {

    public static final String DEFAULT_EMAIL = "foo@bar.com";
    public static final String DEFAULT_EMAIL_REPEAT = "foo@bar.com";
    public static final String DEFAULT_PASSWORD = "my-password";
    public static final String DEFAULT_PASSWORD_REPEAT = "my-password";

    @Builder(builderMethodName = "aRegistrationRequest", setterPrefix = "with")
    public static RegistrationRequest buildRegistrationRequest(String email, String emailRepeat, String password, String passwordRepeat) {
        final RegistrationRequest request = new RegistrationRequest();
        request.setEmail(email);
        request.setEmailRepeat(emailRepeat);
        request.setPasswort(password);     
        return request;
    }
}

这让我可以创建这样的对象:

SomeTestUtils.aRegistrationRequest()
  .withEmail("foo@bar.com")
  .withEmailRepeat("foo@bar.com")
  .withPassword("my-password")
  .withPasswordRepeat("my-password")
.build();

但现在我想为任何未明确设置的字段提供默认值。所以如果我这样做,那么将创建一个具有所有默认值的对象:

SomeTestUtils.aRegistrationRequest().build();

这里是 Lombok 开发者:

你不能,不能直接。在 lombok 开始做它的事情之前,代码需要在语法上有效 java,并且没有可行的方法(至少,我们从来没有想过可以接受的东西)来写你想要的东西 java语法。这些东西根本行不通:

/* 1 */ void foo(int param = 5) {}
/* 2 */ void foo(@DefaultValue(5) int param) {}
/* 3 */ void foo(@DefaultValue("5") int param) {}
/* 4 */ void foo(int param) {
  defaultValues: {
    param = 5;
  }
}
  1. 这甚至无法解析。 Javac 因语法错误而失败,甚至没有调用 lombok。我们不能这样做。
  2. 注释参数需要特定类型 - 您不能定义采用类型为 'whatever' 的参数的注释。我们可以制作 @IntDefaultValue@StringDefaultValue 等,但是 [A] 只允许您涵盖基元、字符串、Class<?> 值、枚举和这些类型的数组——除此之外别无其他,并且[B] 这意味着您只能提供编译时常量。这限制太多了。
  3. 这会 'work' 但是在字符串文字中推送实际的 java 代码太丑陋了,我们不会将其添加到 lombok。
  4. 这是最接近可行的,但是您不可能记住您必须完全按照那样来编写它,而且 linting 工具会对此感到相当困惑。它还与许多 java 程序员向每个参数添加 final 的烦人习惯发生冲突(如果你是其中之一,我建议你把它剪掉。如果你讨厌改变参数,添加一个 linting或 IDE 规则来防止它,请从您的代码中删除该混乱)。 defaultValues: 参数也很容易打错,你会得到零个自动完成帮助。

好的,那我该怎么做呢?

如果 null 不是您要为其提供默认值的内容的合理值,请将 null 视为“我想要默认值”。类似于:

@Builder void foo(Integer x) {
  if (x == null) x = 5;
  // carry on with the code as normal
}

或者如果你不想改变你的参数(我再次恳求你 - 健全性检查和默认行为应该应用于参数;不这样做意味着很容易不小心使用 'wrong'一),你可以这样做:

@Builder void foo(Integer paramIn) {
  int param == paramIn == null ? 5 : paramIn;
  // carry on as normal. Woe is you if you use paramIn.
}

当然,我建议您添加一些 java文档来解释这一点。

如果 null 是一个明智的选择,但不应该是默认选项,那么我们主要在 'wow, that is so rare and exotic, it is no longer boilerplate' 范围内。