![](/img/trans.png)
[英]With lombok, can required-arg constructor coexist with @Value, @Builder and @Builder.Default annotations?
[英]Required arguments with a Lombok @Builder
您可以使用 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
在這里是可選的。
我建議不要使用這種方法,因為您將很難在其他對象上始終如一地應用它。 相反,您可以只使用@lombok.NonNull
注釋標記字段,Lombok 將在構造函數和 setter 中為您生成空檢查,以便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()
將凱文·戴 (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()
@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
添加到所有必需值。 如果未設置必填字段,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;
}
}
這是我對問題的解決方案
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();
}
}
}
靈感來自這篇博文:
https://blog.jayway.com/2012/02/07/builder-pattern-with-a-twist/
以User
類為例, 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();
或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);
}
}
最佳實踐:
import lombok.Builder;
import lombok.NonNull;
@Builder(builderMethodName = "privateBuilder")
public class Person {
@NonNull
private String name;
private String surname;
public static class PersonNameBuilder {
public PersonBuilder name(String name) {
return Person.privateBuilder().name(status);
}
}
public static PersonNameBuilder builder(String name) {
return new PersonNameBuilder();
}
private static PersonBuilder privateBuilder(){
return new PersonBuilder();
}
}
用法:
PersonNameBuilder nameBuilder = Person.builder();
PersonBuilder builder = nameBuilder.name("John");
Person p1 = builder.surname("Smith").build();
// Or
Person p2 = Person.builder().name("John").surname("Smith").build();
如果你需要這個功能,你可以自己自定義構建器類,你仍然可以添加@Builder
Annotation。
@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);
}
}
盡管我希望擁有編譯時驗證功能,但該庫的作者已明確表示可能不會添加該功能。
所以我對此的看法是,擁有這樣的東西。
@Builder
public class Person {
String name;
Integer age;
Optional optional;
@Builder
public class Optional {
String surname;
String companyName;
String spouseName;
}
}
你可以像這樣使用它
Person p = Person.builder()
.age(40)
.name("David")
.optional(Person.Optional.builder()
.surname("Lee")
.companyName("Super Company")
.spouseName("Emma")
.build())
.build();
不,沒有驗證。 但是從庫的用戶的角度來看,很清楚什么是必需的,什么不是,並且能夠在不查看文檔的情況下構建對象實例。
這是 Pawel 響應的靈感,帶有一個隱藏的生成構建器:
import lombok.Builder;
import lombok.ToString;
@Builder(builderMethodName = "")
@ToString
public class Person {
private String name;
private String surname;
public static PersonBuilder builder(String name) {
return new PersonBuilder().name(name);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.