使用 Lombok 为 类 创建具有必需和可选属性的构建器
Using Lombok to create builders for classes with required and optional attributes
正在搜索一个插件以避免样板代码来实现 Joshua Bloch's builder pattern I found the amazing Lombok Project,它使您能够通过这样的注释生成构建器:
@Builder
public class Person {
private String name;
private String address;
private String secondAddress;
}
PersonBuilder.builder().name("yourName").address("your Address").build();
如您所见,没有样板代码,您可以通过调用提供的静态 builder()
方法轻松创建 Person
的实例,链接 setter-调用就像它与 JavaBeans-Pattern 一起工作一样,并通过调用 build()
;
结束链
与构建器模式相比,JavaBeans 模式的缺点之一是(来自 Effective Java):
Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction.
假设在上面的示例中,前两个属性 name 和 address 是构造 Person 实例所必需的,Lombok 实现构建器模式的方式使开发人员能够 split/shorten 构造并执行Person
实例可能不一致的东西,例如:
Person p = PersonBuilder.builder().name("yourName").build();
...
System.out.println(p.getAddress());
...
p.setAddress("your address");
Joshua Bloch's solution prefers a builder method with the mandatory attributes as parameters, so that there is no possibility that the construction is split across multiple calls, like illustrated in Item 2: Consider a builder when faced with many constructor parameters.
我的问题是:
正如 Joshua Bloch 所建议的那样,是否有任何方便的方法,如 @Builder 的注释参数或属性级别的 Springs @Required 或 @Mandatory 之类的东西来强制 Lombok 避免提供无参数构建器构造函数并为构造函数提供强制参数?
我尝试了 @Builder documentation 中的许多选项,但找不到理想的解决方案。
对我有用的描述如下:
- 为具有强制参数的 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();
}
}
根据 @Builder
docs,此注释可以与 @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
}
要更进一步,您可以自己定义 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();
}
但是仍然可以通过编写 new Person.PersonBuilder()
来创建构建器,因为构建器 class 仍然可以访问。
此外,在扩展中可以使用:
@Builder.Default <modifier><instanceVariable>=<default-value>
、
赞:@Builder.Default private String myVariable = ""
。
请阅读:@Builder default properties
避免编译器错误询问在某些情况下不会设置的必需属性,例如持久性、索引等(update/deleteBy/findBy 来自 id 或简化的属性集,没有完整的对象图)。
这是一个优雅的解决方案,不按照建议覆盖 .build() 或 setters。
正在搜索一个插件以避免样板代码来实现 Joshua Bloch's builder pattern I found the amazing Lombok Project,它使您能够通过这样的注释生成构建器:
@Builder
public class Person {
private String name;
private String address;
private String secondAddress;
}
PersonBuilder.builder().name("yourName").address("your Address").build();
如您所见,没有样板代码,您可以通过调用提供的静态 builder()
方法轻松创建 Person
的实例,链接 setter-调用就像它与 JavaBeans-Pattern 一起工作一样,并通过调用 build()
;
与构建器模式相比,JavaBeans 模式的缺点之一是(来自 Effective Java):
Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction.
假设在上面的示例中,前两个属性 name 和 address 是构造 Person 实例所必需的,Lombok 实现构建器模式的方式使开发人员能够 split/shorten 构造并执行Person
实例可能不一致的东西,例如:
Person p = PersonBuilder.builder().name("yourName").build();
...
System.out.println(p.getAddress());
...
p.setAddress("your address");
Joshua Bloch's solution prefers a builder method with the mandatory attributes as parameters, so that there is no possibility that the construction is split across multiple calls, like illustrated in Item 2: Consider a builder when faced with many constructor parameters.
我的问题是: 正如 Joshua Bloch 所建议的那样,是否有任何方便的方法,如 @Builder 的注释参数或属性级别的 Springs @Required 或 @Mandatory 之类的东西来强制 Lombok 避免提供无参数构建器构造函数并为构造函数提供强制参数?
我尝试了 @Builder documentation 中的许多选项,但找不到理想的解决方案。
对我有用的描述如下:
- 为具有强制参数的 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();
}
}
根据 @Builder
docs,此注释可以与 @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
}
要更进一步,您可以自己定义 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();
}
但是仍然可以通过编写 new Person.PersonBuilder()
来创建构建器,因为构建器 class 仍然可以访问。
此外,在扩展中可以使用:
@Builder.Default <modifier><instanceVariable>=<default-value>
、
赞:@Builder.Default private String myVariable = ""
。
请阅读:@Builder default properties
避免编译器错误询问在某些情况下不会设置的必需属性,例如持久性、索引等(update/deleteBy/findBy 来自 id 或简化的属性集,没有完整的对象图)。
这是一个优雅的解决方案,不按照建议覆盖 .build() 或 setters。