Lombok est une librairie sous licence MIT permettant de générer du code lors de la compilation des classes JAVA. A travers l’utilisation d’annotations, Lombok permet d’obtenir un code plus lisible et libère également le développeur de la longueur d’écriture des méthodes que l’on retrouve communément dans les classes objets et métiers d’un projet.
Comment installer Lombok
Lombok est actuellement disponible en version 1.16.18 sortie en juillet 2017. Les mises à jour régulières de la librairie témoignent de sa pérennité. Elle s’intègre facilement à un projet existant :
Utilisation avec Maven
1 2 3 4 5 6 7 |
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> <scope>provided</scope> </dependency> |
Utilisation avec Gradle
1 2 |
// https://mvnrepository.com/artifact/org.projectlombok/lombok provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18' |
Comment utiliser Lombok
Prenons une classe Java qui permet de gérer des informations simples d’un utilisateur : nom, prénom et âge.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
/** * Classe utilisateur. */ public class UtilisateurBean { /** Age de l'utilisateur. */ private Integer _age; /** Nom de l'utilisateur. */ private String _nom; /** Prénom de l'utilisateur. */ private String _prenom; /** * Constructeur vide. */ public UtilisateurBean() { } /** * Constructeur avec initialisation des propriétés. * @param pNom * Le nom. * @param pPrenom * Le prénom. * @param pAge * L'age. */ public UtilisateurBean(final String pNom, final String pPrenom, final Integer pAge) { this._nom = pNom; this._prenom = pPrenom; this._age = pAge; } /** * @return l'age de l'utilisateur. */ public Integer getAge() { return this._age; } /** * @return le nom de l'utilisateur. */ public String getNom() { return this._nom; } /** * @return le prénom de l'utilisateur. */ public String getPrenom() { return this._prenom; } /** * Affectation de l'age. * @param pAge * L'age de l'utilisateur. */ public void setAge(final Integer pAge) { this._age = pAge; } /** * Affectation du nom. * @param pNom * Le nom de l'utilisateur. */ public void setNom(final String pNom) { this._nom = pNom; } /** * Affectation du prénom. * @param pPrenom * Le prénom de l'utilisateur. */ public void setPrenom(final String pPrenom) { this._prenom = pPrenom; } } |
Difficile de faire une classe plus simple que celle-ci ! Pourtant, elle comporte déjà environ 80 lignes. En tant que développeur, nous écrivons souvent ce type de classe dont la majeure partie est systématiquement structurée de la même façon. C’est ici que la mise en place de Lombok prend tout son intérêt.
@Getter et @Setter
L’utilisation des annotations @Getter et @Setter permet de ne plus s’occuper de l’écriture des accesseurs de la classe. Nous obtenons déjà un objet plus lisible :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/** * Classe utilisateur. */ @Getter @Setter public class UtilisateurBean { /** Age de l'utilisateur. */ private Integer _age; /** Nom de l'utilisateur. */ private String _nom; /** Prénom de l'utilisateur. */ private String _prenom; /** * Constructeur vide. */ public UtilisateurBean() { } /** * Constructeur avec initialisation des propriétés. * @param pNom * Le nom. * @param pPrenom * Le prénom. * @param pAge * L'age. */ public UtilisateurBean(final String pNom, final String pPrenom, final Integer pAge) { this._nom = pNom; this._prenom = pPrenom; this._age = pAge; } } |
Il est possible de rendre le getter d’une propriété « lazy », c’est-à-dire d’exécuter son affectation uniquement lors du premier appel. Pour cela, il suffit d’ajouter @Getter(lazy=true) sur la propriété, utilisé principalement sur les initialisations coûteuses en mémoire ou en processeur.
1 2 3 4 5 6 7 8 9 10 |
@Getter(lazy=true) private final float lazyPropriete = initialisationLazy(); private float initialisationLazy() { float resultat = 0; // … // Calcul complexe. // … return resultat; } |
@Accessors
A l’utilisation de la classe, on remarque alors que les getters et les setters sont légèrement différents de ceux que nous avions écrits au préalable. En effet, nous obtenons pour le prénom : get_prenom() au lieu de getPrenom(). Ceci est dû au caractère « _ » que nous avons ajouté devant chaque propriété. Pour rester cohérent avec les éventuelles normes de codage du projet, il suffit d’ajouter dans le cas présent l’annotation @Accessors(prefix = « _ ») qui permet d’obtenir le getter getPrenom() initialement écrit.
@NoArgsConstructor et @AllArgsConstructor
Il est également possible de s’abstraire de l’écriture des constructeurs de classe. Dans le cas présenté ici, nous allons gérer un constructeur sans arguments, et un autre constructeur contenant l’initialisation de toutes les propriétés. Avec l’utilisation supplémentaire de ces deux annotations, nous obtenons l’équivalent de la classe de départ avec seulement une vingtaine de lignes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Classe utilisateur. */ @Getter @Setter @Accessors(prefix = "_") @NoArgsConstructor @AllArgsConstructor public class UtilisateurBean { /** Age de l'utilisateur. */ private Integer _age; /** Nom de l'utilisateur. */ private String _nom; /** Prénom de l'utilisateur. */ private String _prenom; } |
A noter qu’il est possible de spécifier le niveau de visibilité du constructeur complet, en ajoutant à l’annotation l’information suivante :
@AllArgsConstructor(access = AccessLevel.PROTECTED)
Il peut arriver que les propriétés d’une classe soient définies en « static ». Dans ce cas, une erreur de compilation apparait. Ceci est dû au fait qu’une propriété « static » doit être initialisée. Pour éviter l’erreur de compilation, il faut ajouter l’argument « force = true » à l’annotation @NoArgsConstructor :
@NoArgsConstructor( force = true )
Ceci a pour effet d’initialiser les propriétés avec la valeur 0, false ou null.
@RequiredArgsConstructor(staticName= »of »)
Pour réaliser un constructeur plus spécifique, il est possible de déterminer quelles propriétés doivent être initialisées lors de l’instanciation de la classe. Pour cela, il suffit de suivre deux étapes :
– ajouter l’annotation @NonNull aux propriétés concernées
– ajouter l’annotation @RequiredArgsConstructor(staticName= « of »)
Où « of » correspond au nom du nouveau constructeur. Exemple avec notre classe :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Classe utilisateur. */ @Getter @Setter @Accessors(prefix = "_") @RequiredArgsConstructor(staticName = "of") public class UtilisateurBean { /** Age de l'utilisateur. */ private Integer _age; /** Nom de l'utilisateur. */ @NonNull private String _nom; /** Prénom de l'utilisateur. */ private String _prenom; } |
Pour l’utiliser, on écrit un code semblable à :
UtilisateurBean vUtilisateur = UtilisateurBean.of(« admin ») ;
L’inconvénient est qu’il n’est pas possible avec cette méthode de définir plusieurs constructeurs particuliers. Pour cela, il reste toujours la solution de l’écriture manuelle.
@Builder
Cette annotation génère un « builder » permettant de ne plus être dépendant de l’ordre particulier des paramètres lors de l’instanciation d’une classe. Dans notre exemple, il est donc possible d’écrire le code suivant :
UtilisateurBean.builder().nom(«unNom»).prenom(«unPrenom»).build();
L’inconvénient est qu’aucune erreur de compilation n’est remontée si un paramètre est manquant. De plus, lors d’une revue de code par exemple, la recherche de la hiérarchie d’appel est plus contraignante à analyser.
@Equals @HashCode et @ToString
Ces annotations permettent à Lombok de générer respectivement les méthodes « equals », « hashcode » et « ToString ». L’intérêt d’utiliser ces annotations, en plus d’éviter de les coder, réside dans le paramétrage des attributs que l’on souhaite utiliser dans ces méthodes. Pour cela, il y a deux manières de faire : soit par sélection en utilisant le paramètre « of », soit par exclusion en utilisant le paramètre « exclude ». Il est possible ainsi de définir précisément le comportement souhaité de la classe. Voici deux exemples qui illustrent l’utilisation de ces annotations pour que les méthodes correspondantes ne prennent en compte que les propriétés « nom » et « prenom » :
– Méthode ToString() : @ToString(of = {« nom », « prenom »}) ou @ToString(exclude = {« age »})
– Méthode HashCode() : @HashCode(of = {« nom », « prenom »}) ou @HashCode(exclude = {« age »})
– Méthode Equals() : @Equals(of = {« nom », « prenom »}) ou @Equals(exclude = {« age »})
Conclusion
La plupart des environnements de développement proposent la réécriture de code permettant au développeur de ne pas coder manuellement les méthodes redondantes d’une classe métier Java. Un des principaux avantages qu’offre Lombok réside dans l’utilisation des annotations permettant d’améliorer la lisibilité du code, en ne générant les méthodes que dans les classes compilées. De plus, les classes gagnent en maintenabilité, par exemple lors de la modification d’un nom de variable qui ne se fait qu’au niveau de la propriété.
Il faut noter cependant quelques inconvénients. Une fois les annotations Lombok mises en place, la navigation dans la hiérarchie des appels (de getter et setter par exemple) n’est plus aussi directe. Sous Eclipse, une solution de contournement consiste à lancer les recherches depuis la vue « outlines ». De plus, la Javadoc est également très succincte. Il faut penser à écrire la documentation complète sur la propriété directement.
Un autre piège à éviter est celui de la surcharge d’annotation. En effet, cette solution populaire utilisée par de nombreux Frameworks comme Hibernate, Spring ou Lombok transforme nos classes en une pile d’annotations donnant un résultat parfois contraire à celui attendu, à savoir simplifier l’écriture et la compréhension.
Site internet : https://projectlombok.org/
Il est possible de retrouver l’intégralité de cet article dans le numéro 217 du mois d’Avril 2018 du magazine « Programmez ».