使用lombok @Builder的必需参数

jax*_*jax 54 java lombok

如果我将@Builder添加到类中.构建器方法已创建.

Person.builder().name("john").surname("Smith").build();
Run Code Online (Sandbox Code Playgroud)

我有一个要求需要特定字段的要求.在这种情况下,名称字段是必需的,但姓氏不是.理想情况下,我想宣布它是这样的.

Person.builder("john").surname("Smith").build()
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚如何做到这一点.我尝试将@Builder添加到构造函数中,但它不起作用.

@Builder
public Person(String name) {
    this.name = name;
}
Run Code Online (Sandbox Code Playgroud)

Paw*_*wel 62

您可以使用Lombok注释配置轻松完成

import lombok.Builder;
import lombok.ToString;

@Builder(builderMethodName = "hiddenBuilder")
@ToString
public class Person {

    private String name;
    private String surname;

    public static PersonBuilder builder(String name) {
        return hiddenBuilder().name(name);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后就这样使用它

Person p = Person.builder("Name").surname("Surname").build();
System.out.println(p);
Run Code Online (Sandbox Code Playgroud)

当然@ToString这里是可选的.

  • 这个答案对我来说没有意义的是hiddenBuilder()没有隐藏...... (22认同)
  • 我只是告诉 Lombok 将构建器设为私有:`@Builder(builderMethodName = "hiddenBuilder", access = AccessLevel.PRIVATE)` (10认同)
  • @AkshatAgarwal是什么让隐藏的构建器方法?我99.99%确定builderMethodName只是更改方法的名称 - 它不会将方法更改为隐藏.所以我仍然没有看到任何方法来实现所需的字段的预期结果. (4认同)
  • @Linus 似乎添加 AccessLevel.PRIVATE 也使构建器的所有方法都是私有的,并且认为它毫无用处。我错了吗? (3认同)
  • 如果你能解释一下会更好. (2认同)
  • @DeanGurvitz 忘记更新 IntelliJ Idea 的 Lombok 插件。你是对的,所有方法都是私有的,所以这种方法不起作用。 (2认同)
  • 从 lombok v1.18.8 开始,您可以执行 `@Builder(builderMethodName = "")`,使 lombok 现在为您创建任何构建器方法。然后您可以自己创建它,如本答案所示。 (2认同)
  • 要使构建器私有,您可以简单地使用:`@Builder(builderMethodName = "")`并在方法内使用他的构造函数实例化它:`public static PersonBuilder builder(String name) { return new PersonBuilder().name(name) ; }` (2认同)

Ant*_*jev 30

我建议不要采用这种方法,因为你很难在其他对象上一致地应用它.相反,您只需使用@lombok.NonNull注释标记字段,Lombok将在构造函数和设置器中为您生成空检查,因此Builder.build()如果未设置这些字段,则会失败.

使用构建器模式可以非常清楚地标识要设置哪些值的字段.对于您的示例中的名称字段,这已经丢失,如果您正在构建具有多个必填字段的对象,则所有其他必填字段将进一步丢失.考虑以下示例,您能通过阅读代码来判断哪个字段是哪个?

Person.builder("John", "Michael", 16, 1987) // which is name, which is surname? what is 16?
    .year(1982) // if this is year of birth, then what is 1987 above?
    .build()
Run Code Online (Sandbox Code Playgroud)

  • 运行时错误与编译时错误.总是喜欢编译时错误! (13认同)
  • @jax他们不会检查同一件事。要求设置字段不会检查空值。无论您是否需要该字段,检查null都是运行时错误(在纯Java中)。 (4认同)
  • 您也可以使用先前的答案构造空字段,例如Person.builder(null).lastName(“ John”)。build();。因此无论如何您仍然需要运行时检查。 (3认同)
  • @LakatosGyula,我确实同意,但我认为按照惯例,开发人员用 null 调用所需字段的机会较小,使用常规构建器可能会出现更多错误,这是一种权衡,我会同意 Anton 的答案。 (2认同)

The*_*ger 14

将Kevin Day的答案更进一步:

@Builder
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE) // If immutability is desired
@ToString
public class Person {
    @NonNull // Presumably name cannot be null since its required by the builder
    private final String name;
    private final String surname;

    private static PersonBuilder builder() {
        return new PersonBuilder();
    }

    public static PersonBuilder builder(String name){
        return builder().name(name);
    }

}
Run Code Online (Sandbox Code Playgroud)

它并不理想,但它提供了编译时执行功能,此类的调用者将只使用一种构建器方法.

  • 很好,但仍然可以通过调用 `.name()` 来覆盖该值(错误地),因此我们将得到: `Person.builder("john").surname("smith").name("mark") .build();` 当然最终我们会得到 `mark` 而不是想要的 `john`。我知道这是一个有点牵强的情况,但也是可能的,完美的解决方案将避免这种情况。您认为可以对此进行防御吗?(我现在正在处理这个问题) (3认同)

Kev*_*Day 13

这是另一种方法:

@Builder()
@Getter
@ToString
public class Person {

    private final String name;
    private final String surname;

    public static PersonBuilder builder(String name){
        return new PersonBuilder().name(name);
    }

    public static void main(String[] args) {
        Person p = Person.builder("John Doe")
                .surname("Bill")
                .build();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法的问题是builder()仍然是可见的,因此它并不*真正*使所需的参数.无论如何,是的,我们还需要使用@NonNull注释 - 但这是一个运行时检查 - 如果一个人试图制作一个超级直观的对象,那肯定不够好.令人遗憾的是,没有办法在lombok中定制这种东西 - 即使我们可以设法使builder()方法变为私有,我们也可以创建自己的重载公共构建器(...)方法有必要的参数. (5认同)
  • 显然,过去五年中发生了一些变化,因此这种方法将不再可用 builder() 。与公认的 anwear 相比,这也没有给我一个非隐藏的hiddenBuilder(),这使得这是我最喜欢的方法。 (2认同)

lil*_*nux 10

最简单的解决方案是将 a 添加@lombok.NonNull到所有必需值。如果未设置必填字段,Builder 将无法构建对象。

这里有一个JUnit测试来证明的所有组合的行为final@NonNull

import static org.junit.Assert.fail;

import org.junit.Test;

import lombok.Builder;
import lombok.ToString;

public class BuilderTests {
    @Test
    public void allGiven() {
        System.err.println(Foo.builder()
            .nonFinalNull("has_value")
            .nonFinalNonNull("has_value")
            .finalNull("has_value")
            .finalNonNull("has_value")
            .build());
    }

    @Test
    public void noneGiven() {
        try {
            System.err.println(Foo.builder()
                .build()
                .toString());
            fail();
        } catch (NullPointerException e) {
            // expected
        }
    }

    @Test
    public void nonFinalNullOmitted() {
        System.err.println(Foo.builder()
            .nonFinalNonNull("has_value")
            .finalNull("has_value")
            .finalNonNull("has_value")
            .build());
    }

    @Test
    public void nonFinalNonNullOmitted() {
        try {
            System.err.println(Foo.builder()
                .nonFinalNull("has_value")
                .finalNull("has_value")
                .finalNonNull("has_value")
                .build());
            fail();
        } catch (NullPointerException e) {
            // expected
        }
    }

    @Test
    public void finalNullOmitted() {
        System.err.println(Foo.builder()
            .nonFinalNull("has_value")
            .nonFinalNonNull("has_value")
            .finalNonNull("has_value")
            .build());
    }

    @Test
    public void finalNonNullOmitted() {
        try {
            System.err.println(Foo.builder()
                .nonFinalNull("has_value")
                .nonFinalNonNull("has_value")
                .finalNull("has_value")
                .build());
            fail();
        } catch (NullPointerException e) {
            // expected
        }
    }

    @Builder
    @ToString
    private static class Foo {
        private String nonFinalNull;

        @lombok.NonNull
        private String nonFinalNonNull;

        private final String finalNull;

        @lombok.NonNull
        private final String finalNonNull;
    }
}
Run Code Online (Sandbox Code Playgroud)


koz*_*a13 5

这是我解决问题的方法

import lombok.Builder;
import lombok.Data;
import lombok.NonNull;

@Data
@Builder(builderMethodName = "privateBuilder")
public class Person {
    @NonNull
    private String name;
    @NonNull
    private String surname;
    private int age;//optional

public static Url safeBuilder() {
    return new Builder();
}

interface Url {
    Surname name(String name);
}

interface Surname {
    Build surname(String surname);
}

interface Build {
    Build age(int age);
    Person build();
}

public static class Builder implements Url, Surname, Build {
    PersonBuilder pb = Person.privateBuilder();

    @Override
    public Surname name(String name) {
        pb.name(name);
        return this;
    }

    @Override
    public Build surname(String surname) {
        pb.surname(surname);
        return this;

    }

    @Override
    public Build age(int age) {
        pb.age(age);
        return this;
    }

    @Override
    public Person build() {
        return pb.build();
    }
    }
}
Run Code Online (Sandbox Code Playgroud)

灵感来自这篇博文:

https://blog.jayway.com/2012/02/07/builder-pattern-with-a-twist/

  • 这就是我想为lombok为我生成的内容. (11认同)