Lombok Builder avec valeur par défaut

Lombok Builder avec valeur par défaut

1. introduction

Dans ce rapide didacticiel, nous allons étudier comment fournir des valeurs par défaut pour les attributs lors de l'utilisation du modèle de générateur avecLombok.

Assurez-vous également de consulter nosintro to Lombok.

2. Les dépendances

Nous utiliseronsLombok dans ce tutoriel, et pour cela, nous n'avons besoin que d'une seule dépendance:


    org.projectlombok
    lombok
    1.16.18
    provided

3. POJO avec Lombok Builder

Voyons d'abord comment Lombok peut nous aider à nous débarrasser du code standard nécessaire à la mise en œuvre du modèle de générateur.

Nous allons commencer par un simple POJO:

public class Pojo {
    private String name;
    private boolean original;
}

Pour que ce cours soit utile, nous aurons besoin de getters. De plus, par exemple, si nous souhaitons utiliser cette classe avec un ORM, nous aurons probablement besoin d’un constructeur par défaut.

En plus de cela, nous voulons un constructeur pour cette classe. Avec Lombok, nous pouvons avoir tout cela avec quelques annotations simples:

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Pojo {
    private String name;
    private boolean original;
}

4. Définir les attentes

Définissons quelques attentes pour ce que nous voulons réaliser sous la forme de tests unitaires.

La première exigence de base est la présence de valeurs par défaut après la construction d'un objet avec un générateur:

@Test
public void givenBuilderWithDefaultValue_ThanDefaultValueIsPresent() {
    Pojo build = Pojo.builder()
        .build();
    Assert.assertEquals("foo", build.getName());
    Assert.assertTrue(build.isOriginal());
}

Of course, this test fails since the @Builder annotation doesn’t populate values. Nous allons résoudre ce problème bientôt.

Si nous utilisons un ORM, il s’appuie généralement sur un constructeur par défaut. Donc, nous devrions nous attendre au même comportement du constructeur par défaut que du constructeur:

@Test
public void givenBuilderWithDefaultValue_NoArgsWorksAlso() {
    Pojo build = Pojo.builder()
        .build();
    Pojo pojo = new Pojo();
    Assert.assertEquals(build.getName(), pojo.getName());
    Assert.assertTrue(build.isOriginal() == pojo.isOriginal());
}

A ce stade, ce test est réussi.

Voyons maintenant comment faire réussir les deux tests!

5. AnnotationBuilder.Default de Lombok

Depuis Lombok v1.16.16, nous pouvons utiliser l'annotation interne de@Builder:

// class annotations as before
public class Pojo {
    @Builder.Default
    private String name = "foo";
    @Builder.Default
    private boolean original = true;
}

C'est simple et lisible, mais il présente quelques défauts.

Avec cela, les valeurs par défaut seront présentes avec le générateur, faisant passer le premier cas de test. Unfortunately, the no-args constructor won’t get the default values, making the second test case fail. Même si le constructeur no-args n'est pas généré mais écrit explicitement.

Cet effet secondaire de l'annotationBuilder.Default est présent depuis le début et il le restera probablement pendant longtemps.

6. Initialiser le générateur

Nous pouvons essayer de faire passer les deux tests en définissant des valeurs par défaut dans une implémentation de générateur minimaliste:

// class annotations as before
public class Pojo {
    private String name = "foo";
    private boolean original = true;

    public static class PojoBuilder {
        private String name = "foo";
        private boolean original = true;
    }
}

De cette façon, les deux tests réussiront.

Malheureusement, le prix est la duplication de code. For a POJO with tens of fields, it could be error prone to maintain the double initialization.

Mais, si nous sommes prêts à payer ce prix, nous devons également nous occuper d’une dernière chose. Si nous renommons notre classe en utilisant une refactorisation dans notre IDE, la classe interne statique ne sera pas automatiquement renommée. Ensuite, Lombok ne le trouvera pas et notre code se cassera.

Pour éliminer ce risque, nous pouvons décorer l'annotation de générateur:

// class annotations as before
@Builder(builderClassName = "PojoBuilder")
public class Pojo {
    private String name = "foo";
    private boolean original = true;

    public static class PojoBuilder {
        private String name = "foo";
        private boolean original = true;
    }
}

7. Utilisation detoBuilder

@Builder prend également en charge la génération d'une instance du générateur à partir d'une instance de la classe d'origine. Cette fonctionnalité n'est pas activée par défaut. Nous pouvons l'activer en définissant le paramètretoBuilder dans l'annotation du générateur:

// class annotations as before
@Builder(toBuilder = true)
public class Pojo {
    private String name = "foo";
    private boolean original = true;
}

Avec cela,we can get rid of the double initialization.

Bien sûr, il y a un prix à payer pour cela. We have to instantiate the class to create a builder. Donc, nous devons également modifier nos tests:

@Test
public void givenBuilderWithDefaultValue_ThenDefaultValueIsPresent() {
    Pojo build =  new Pojo().toBuilder()
        .build();
    Assert.assertEquals("foo", build.getName());
    Assert.assertTrue(build.isOriginal());
}

@Test
public void givenBuilderWithDefaultValue_thenNoArgsWorksAlso() {
    Pojo build = new Pojo().toBuilder()
        .build();
    Pojo pojo = new Pojo();
    Assert.assertEquals(build.getName(), pojo.getName());
    Assert.assertTrue(build.isOriginal() == pojo.isOriginal());
}

Encore une fois, les deux tests réussissent, nous avons donc la même valeur par défaut en utilisant le constructeur no-args que lors de l'utilisation du générateur.

8. Conclusion

Nous avons donc examiné plusieurs options pour fournir des valeurs par défaut pour le générateur Lombok.

L'effet secondaire de l'annotationBuilder.Default mérite d'être surveillé. Mais les autres options ont aussi leurs inconvénients. Nous devons donc choisir avec soin en fonction de la situation actuelle.

Comme toujours, le code est disponibleover on GitHub.