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 avec Lombok .

N'oubliez pas de consulter également notre introduction à Lombok.

2. Dépendances

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

 org.projectlombok lombok 1.18.10 provided 

3. POJO avec Lombok Builder

Tout d'abord, voyons comment Lombok peut nous aider à nous débarrasser du code standard nécessaire pour implémenter le modèle de générateur.

Nous allons commencer par un simple POJO:

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

Pour que cette classe soit utile, nous aurons besoin de getters. Aussi, 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éfinition des 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()); }

Bien sûr, ce test échoue car l' annotation @Builder ne remplit pas les valeurs. Nous allons résoudre ce problème bientôt.

Si nous utilisons un ORM, il repose 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()); }

À ce stade, ce test réussit.

Voyons maintenant comment faire passer les deux tests!

5. Annotation de Lombok's Builder.Default

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 a 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. Malheureusement, le constructeur no-args n'obtiendra pas les valeurs par défaut, ce qui entraînera l'échec du deuxième cas de test . Même si le constructeur no-args n'est pas généré mais écrit explicitement.

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

6. Initialisez 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 constructeur 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. Pour un POJO avec des dizaines de champs, il peut être sujet à des erreurs de maintenir la double initialisation.

Mais, si nous sommes prêts à payer ce prix, nous devrions également nous occuper d'une autre 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 du constructeur:

// 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 de toBuilder

@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ètre toBuilder 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, nous pouvons nous débarrasser de la double initialisation .

Bien sûr, il y a un prix pour cela. Nous devons instancier la classe pour créer un générateur. 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 constructeur Lombok.

L'effet secondaire du constructeur . Les annotations par défaut méritent d'être surveillées. Mais les autres options ont aussi leurs inconvénients. Nous devons donc choisir soigneusement en fonction de la situation actuelle.

Comme toujours, le code est disponible sur sur GitHub.