使用 Lombok 为具有必需和可选属性的类创建构建器

Ara*_*gir 5 java builder lombok

搜索插件以避免样板代码来实现Joshua Bloch构建器模式,我发现了令人惊叹的Lombok 项目,它使您能够通过这样的注释生成构建器:

@Builder
public class Person {
    private String name;
    private String address;
    private String secondAddress;
}

PersonBuilder.builder().name("yourName").address("your Address").build();
Run Code Online (Sandbox Code Playgroud)

如您所见,没有样板代码,您可以Person通过调用提供的静态builder()方法轻松创建一个实例,链接 setter-like-calls 就像它与 JavaBeans-Pattern 一起工作一样,并以调用结束链build()

与构建器模式相比,JavaBeans-Pattern 的缺点之一是(来自Effective Java):

由于构造被拆分为多个调用,因此JavaBean 在其构造过程中可能处于不一致的状态

假设在上面的示例中,前两个属性 name 和 address 是构造 Person 实例所必需的,Lombok 实现构建器模式的方式使开发人员能够拆分/缩短构造并使用可能不一致的实例执行某些操作Person, 像这样:

Person p = PersonBuilder.builder().name("yourName").build();
...
System.out.println(p.getAddress());
...
p.setAddress("your address");
Run Code Online (Sandbox Code Playgroud)

Joshua Bloch的解决方案更喜欢使用强制属性作为参数的构建器方法,这样就不可能将构造拆分到多个调用中,如第 2 条:当面对许多构造函数参数时考虑构建器

我的问题是: 是否有任何方便的方法,例如 @Builder 的注释参数或属性级别上的 Springs @Required 或 @Mandatory 之类的东西,以强制 Lombok 避免提供无参数的构建器构造函数并提供具有强制参数的构造函数,如 Joshua Bloch提议?

我尝试了@Builder 文档中的许多选项,但找不到理想的解决方案。

对我有用的描述如下:

  • 为具有强制参数的 Person 定义构造函数,
  • 使用强制参数的参数化签名覆盖构建器构造函数。

这有点像样板,这是可以避免的。请参阅我在 Joshua Bloch 的示例中应用的解决方案。

/**
 * Uncle Bobs builder example for constructors with many required & optional parameters,
 * realized by lombok.
 * 
 */
@AllArgsConstructor(access=AccessLevel.PRIVATE) // Let lombok generate private c-tor with all parameters, as needed by @Builder.
@Builder(
        builderClassName="Builder", // Cosmetic. Without this option, the builder class would have the name NutritionFactsBuilder.
        toBuilder=true // Enabling creation of a builder instance based on a main class instance: NutritionFacts. 
)
public class NutritionFacts {

    // Required parameters
    private int servingSize;
    private int servings;

    // Optional parameters
    private int calories;
    private int fat;
    private int sodium;
    private int carbohydrate;

    /**
     * A builder method demanding required parameters.
     */
    public static Builder builder(int servingSize, int servings) {
        return new NutritionFacts(servingSize, servings).toBuilder();
    }

    /**
     * eclipse-created C-tor with required parameters.
     * Can be public for instantiating, but doesn't have to.
     */
    public NutritionFacts(int servingSize, int servings) {
        super();
        this.servingSize = servingSize;
        this.servings = servings;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = NutritionFacts.builder(240, 8)
                                                .calories(100)
                                                .sodium(35)
                                                .carbohydrate(27)
                                                .build();
    }
}
Run Code Online (Sandbox Code Playgroud)

Kar*_*cki 7

根据@Builder文档,此注释可以与@NonNull. 如果标记的字段@NonNull是,null您将NullPointerException阻止创建无效对象:

@Builder
static class Person {
  @NonNull
  private final String name;
  @NonNull
  private final Integer age;
}

public static void main(String[] args) {
  Person.builder()
        .name("Fred")
        .build(); // java.lang.NullPointerException: age is marked @NonNull but is null
}
Run Code Online (Sandbox Code Playgroud)

要更进一步,您可以builder自己定义方法。如果该方法存在 Lombok 将不会生成它,您现在可以强制参数编译时间。

@Builder
static class Person {
  @NonNull
  private final String name;
  @NonNull
  private final Integer age;

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

public static void main(String[] args) {
  Person.builder("Fred", 11)
        .build();
}
Run Code Online (Sandbox Code Playgroud)

但是,仍然可以通过编写来创建构建器,new Person.PersonBuilder()因为构建器类仍然可以访问。


Fel*_*lli 1

另外,在扩展中可以使用:

@Builder.Default <modifier><instanceVariable>=<default-value>,

喜欢:@Builder.Default private String myVariable = ""

请阅读:@Builder 默认属性

避免编译器错误询问在某些情况下不会设置的必需属性,例如持久性、索引等(从 id 或减少的属性集更新/deleteBy/findBy,没有完整的对象图)。

这是一个优雅的解决方案,不会按照建议重写.build()setter