@ Builder をクラスに追加した場合。ビルダーメソッドが作成されます。
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
はここではオプションです。
他のオブジェクトに一貫して適用するのに苦労するため、このアプローチにはお勧めしません。代わりに、フィールドを_@lombok.NonNull
_注釈でマークするだけで、Lombokはコンストラクターとセッターでnullチェックを生成するため、これらのフィールドが設定されていない場合、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()
_
Kevin Dayの answer さらに一歩:
@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);
}
}
理想的ではありませんが、コンパイル時間の強制を提供し、このクラスの呼び出し元には使用するビルダーメソッドが1つだけあります。
別のアプローチを次に示します。
@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();
}
}
これは私の問題の解決策です
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/
最も簡単な解決策は、すべての必須値に@lombok.NonNull
を追加することです。必須フィールドが設定されていない場合、Builderはオブジェクトのビルドに失敗します。
以下は、final
と@NonNull
のすべての組み合わせの動作を示すJUnitテストです。
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;
}
}
この機能が必要な場合は、自分でビルダークラスをカスタマイズできますが、@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);
}
}