web-dev-qa-db-ja.com

ロンボク@Builderで必要な引数

@ Builder をクラスに追加した場合。ビルダーメソッドが作成されます。

Person.builder().name("john").surname("Smith").build();

特定のフィールドが必要な要件があります。この場合、名前フィールドは必須ですが、姓は必須ではありません。理想的には、そのように宣言したいと思います。

Person.builder("john").surname("Smith").build()

私はこれを行う方法を見つけることができません。 @Builderをコンストラクターに追加しようとしましたが、機能しませんでした。

@Builder
public Person(String name) {
    this.name = name;
}
50
jax

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はここではオプションです。

58
Pawel

他のオブジェクトに一貫して適用するのに苦労するため、このアプローチにはお勧めしません。代わりに、フィールドを_@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()
_
27
Anton Koscejev

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();
    }
}
12
Kevin Day

これは私の問題の解決策です

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/

5
kozla13

最も簡単な解決策は、すべての必須値に@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;
    }
}
2
lilalinux

この機能が必要な場合は、自分でビルダークラスをカスタマイズできますが、@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);
    }
}
0
Krishna M