Java Lombok@Builder的必需参数
如果我添加到一个类中。此时将创建生成器方法Java Lombok@Builder的必需参数,java,lombok,Java,Lombok,如果我添加到一个类中。此时将创建生成器方法 Person.builder().name("john").surname("Smith").build(); 我有一个特定领域的要求。在这种情况下,名称字段是必需的,但姓氏不是。理想情况下,我想这样宣布 Person.builder("john").surname("Smith").build() 我想不出怎么做。我尝试将@Builder添加到构造函数中,但没有成功 @Builder public Person(String name) {
Person.builder().name("john").surname("Smith").build();
我有一个特定领域的要求。在这种情况下,名称字段是必需的,但姓氏不是。理想情况下,我想这样宣布
Person.builder("john").surname("Smith").build()
我想不出怎么做。我尝试将@Builder添加到构造函数中,但没有成功
@Builder
public Person(String name) {
this.name = name;
}
您可以使用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);
}
}
然后像那样使用它
Person p = Person.builder("Name").surname("Surname").build();
System.out.println(p);
当然,
@ToString
在这里是可选的。这里有另一种方法:
@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();
}
}
我建议您不要使用这种方法,因为您将很难在其他对象上始终如一地应用它。相反,您可以只使用
@lombok.NonNull
注释标记字段,lombok将在构造函数和设置器中为您生成空检查,因此如果未设置这些字段,则Builder.build()
将失败
使用构建器模式可以非常清楚地标识要设置哪些值的字段。在您的示例中,name字段已经丢失了这个值,如果您正在构建一个包含多个必填字段的对象,那么其他所有必填字段都将丢失这个值。考虑下面的例子,你可以通过读取代码来判断哪个字段是?
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()
这是我解决这个问题的办法
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();
}
}
}
受此博文启发:
最简单的解决方案是在所有强制值中添加
@lombok.NonNull
。未设置必填字段时,生成器将无法生成对象
这里有一个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;
}
}
让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);
}
}
这并不理想,但它提供了编译时强制,该类的调用方只能使用一个生成器方法。如果需要此功能,您可以自己自定义生成器类,并且仍然可以添加
@builder
注释
@Builder
public class Person {
public static class PersonBuilder {
private String name;
private PersonBuilder() {
}
public PersonBuilder(final String name) {
this.name = name;
}
}
private static PersonBuilder builder() {
return null; // or we can throw exception.
}
public static PersonBuilder builder(final String name) {
return new PersonBuilder(clientId);
}
}
以
User
class为例,id
字段为必填项:
@AllArgsConstructor(access = AccessLevel.PRIVATE) // required, see https://stackoverflow.com/questions/51122400/why-is-lombok-builder-not-compatible-with-this-constructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Getter
public class User {
private String id;
private String name;
private int age;
public static UserBuilder builder(final String id) {
return new UserBuilder().id(id);
}
}
您只能通过生成器初始化
User
实例,如User-User=User.builder(“id-123”).name(“Tom”).build代码>。使用private no args构造函数,您无法User User=new User()代码>或用户用户=新用户(“id-123”)代码>因此,您始终需要传递所需的参数id
。请注意,初始化的实例是不可变的。结合@Pawel的答案和Max的注释
import lombok.Builder;
import lombok.ToString;
@Builder
public class Person {
private String name;
private String surname;
public static PersonBuilder builder(String name) {
return new PersonBuilder().name(name);
}
}
最佳做法:
导入lombok.Builder;
导入lombok.NonNull;
@建筑商(builderMethodName=“privateBuilder”)
公共阶层人士{
@非空
私有字符串名称;
私家姓;
公共静态类PersonNameBuilder{
公共PersonBuilder名称(字符串名称){
return Person.privateBuilder().name(状态);
}
}
公共静态PersonNameBuilder(字符串名称){
返回新的PersonNameBuilder();
}
私有静态PersonBuilder私有构建器(){
返回新的PersonBuilder();
}
}
用法:
PersonNameBuilder nameBuilder=Person.builder();
PersonBuilder=nameBuilder.name(“约翰”);
人员p1=建筑商。姓氏(“史密斯”).build();
//或
Person p2=Person.builder().name(“John”).姓氏(“Smith”).build();
如果你能解释得更详细一点就更好了。这个答案对我来说没有意义的是hiddenBuilder()没有隐藏…@AkshatAgarwal是什么让builder方法隐藏的?我99.99%确定builderMethodName只是更改了方法的名称——它不会将方法更改为hidden。因此,我仍然没有找到任何方法来实现所需字段的预期结果。我只想告诉Lombok将生成器设置为私有:@builder(builderMethodName=“hiddenBuilder”,access=AccessLevel.private)
@Linus添加AccessLevel.private似乎也会使所有生成器的方法私有化,并认为它毫无用处。我错了吗?运行时错误和编译时错误。始终支持编译时错误@jax他们不检查相同的东西。要求设置字段不会检查空值。无论您是否需要该字段,检查null都将是运行时错误(在纯Java中)。呃,我现在明白您的意思,但是您的方法也允许null值。最好是预先处理对象契约,而不是让程序员猜测。看看《有效Java第二版》一书中的构建器模式。@NotNull
将在运行时进行评估!您也可以像Person.builder(null.lastName(“John”).build()一样使用前面的答案构造null字段;因此,无论发生什么情况,您仍然需要运行时检查。我更喜欢您的方法,因为它只是重载了builder
方法,使语法简洁自然。这种方法的问题是builder()仍然可见,因此它实际上没有生成所需的参数。当然,是的,我们还需要使用@NonNull注释——但这是一个运行时检查——如果一个人试图创建一个超级直观的对象,那么肯定不够好。令人遗憾的是,在lombok中没有一种方法可以定制这类东西-即使我们可以让builder()方法私有化,我们也可以创建我们自己的重载公共builder(…)方法,并使用所需的参数。显然,在过去五年中发生了一些变化,因此builder()这种方法将不再可用。与公认的anwear不同的是,这也没有给我一个非隐藏的hiddenBuilder(),这使得这成为我最喜欢的方法。感谢@TobiasGrunwald的提醒-这使我的方法非常完美(尽管它确实让我对仍在访问无参数生成器方法的任何人的向后兼容性有点担心!)这就是我希望lombok为我生成的内容。@okutane听起来像