web-dev-qa-db-fra.com

Arguments requis avec un @Builder lombok

Si j'ajoute @Builder à une classe. La méthode du générateur est créée.

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

J'ai une exigence où un domaine particulier est requis. Dans ce cas, le champ Nom est obligatoire mais le nom de famille ne l’est pas. Idéalement, je voudrais le déclarer comme tel. 

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

Je ne sais pas comment faire ça. J'ai essayé d'ajouter @Builder à un constructeur, mais cela n'a pas fonctionné.

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

Vous pouvez le faire facilement avec la configuration d'annotation 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);
    }
}

Et puis l'utiliser comme ça

Person p = Person.builder("Name").surname("Surname").build();
System.out.println(p);

Bien sûr, @ToString est facultatif ici.

48
Pawel

Je recommande contre cette approche, car vous aurez du mal à l'appliquer de manière cohérente sur d'autres objets. Au lieu de cela, vous pouvez simplement marquer les champs avec l'annotation @lombok.NonNull et Lombok générera des contrôles nuls pour vous dans le constructeur et les paramètres, de sorte que Builder.build() échouera si ces champs ne sont pas définis.

L'utilisation du modèle de générateur vous permet d'identifier très clairement quels champs vous définissez et quelles valeurs. Ceci est déjà perdu pour le champ de nom dans votre exemple et sera perdu pour tous les autres champs obligatoires si vous créez un objet avec plusieurs champs obligatoires. Prenons l'exemple suivant, pouvez-vous dire quel champ est lequel simplement en lisant le code?

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()
20
Anton Koscejev

Voici une autre approche:

@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

Ceci est ma solution pour le problème

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

inspiré par cet article de blog:

https://blog.jayway.com/2012/02/07/builder-pattern-with-a-twist/

6
kozla13

Prenant answer de Kevin Day un peu plus loin:

@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);
    }

}

Ce n'est pas idéal, mais cela permet de faire respecter le temps de compilation et les appelants de cette classe auront exactement une méthode de générateur à utiliser.

Voici un test JUnit pour démontrer le comportement de toutes les combinaisons de final et Non Null:

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;
    }
}
1
lilalinux