Au sujet de Lombok
Il y a une annotation pour ça !Il y a quelques mois, notre équipe a accueilli un nouveau venu. En plus de notre mission, il découvrait aussi Java, après quelques années en tant que développeur .NET. Parmi ses premières réactions, il y a eu plusieurs « Quoi, tu dois faire ça toi-même, en Java ? En .NET, c’est le compilateur qui le fait pour toi ! »
Il est vrai qu’en Java, le langage et les conventions amènent une quantité non négligeable de code à faible valeur ajoutée. Mais ne craignez pas ! Le chevalier Lombok vient à votre rescousse.
Ce billet n’est pas un tutoriel, il s’agit simplement d’une présentation rapide de ce que Lombok peut faire pour vous.
L’objectif #
Qu’est-ce que le code boilerplate ? #
Imaginez-vous écrire une classe Java, en respectant toutes les bonnes pratiques. Chaque champ doit être privé et être exposé à travers des accesseurs.
Cela signifie qu’à chaque fois que vous ajoutez un champ, vous devez déclarer de nouveaux getters et setters. Si votre objet est immuable, ils doivent tous être initialisés à l’instanciation. Ceci implique que votre constructeur doit leur fournir une valeur et, en cas de nouveaux champs, vous devrez le mettre à jour également.
C’est ça, le code boilerplate. Il embarque très peu de logique, mais vous devez l’écrire pour lier votre tout et respecter les recommandations. C’est un code frustrant, parce qu’on a souvent l’impression qu’une machine pourrait le faire à notre place (et vous pouvez certainement le générer avec quelques clics ou raccourcis dans votre IDE).
Quand vous venez d’autres langages où tout est implicite ou simplement déclaratif (comme TypeScript ou C#), prendre le temps d’explicitement taper ce code ressemble à une relique d’un autre temps. Cela paraît obsolète, ou tout au mieux pénible.
C’est pour lutter contre ceci que Lombok a été créé.
Quelle solution propose Lombok ? #
Lombok gravite autour des annotations : au lieu d’écrire le boilerplate vous-même, vous déclarez ce que vous voulez avoir dans votre classe.
Par exemple, si vous avez besoin d’un constructeur qui prend un argument par champ, vous annotez votre classe avec @AllArgsConstructor
.
Je vous donne quelques autres exemples plus bas.
Mais qu’arrive-t-il à ces annotations ? On a pris l’habitude qu’elles soient utilisées à l’exécution pour personnaliser le comportement, injecter un aspect ou autre fonctionnalité de ce style (Spring, Jackson, merci pour votre aide). Lombok est cependant différent.
En sus de fournir une collection d’annotations, Lombok est également un processeur d’annotations : s’il est sur le path de votre compilateur Java, il analysera votre code annoté au moment de la compilation et générera les classes en respectant vos indications. Grâce à cela, il vous apporte du confort au moment de l’écriture du code, mais n’a aucun impact sur les performances de votre application puisque la réflexion ne sera pas utilisée.1
Par où commencer ? #
Je préfère insister : ce billet n’est pas un tutoriel. Je ne fais que gratter la surface afin de vous indiquer ce que vous pourrez découvrir si vous creusez plus profondément.
Pour travailler avec Lombok, vous aurez besoin de :
- une dépendance Maven ;
- un plugin pour votre IDE.
C’est parti, entrons dans la matrice.
Configuration Maven #
Rien de compliqué, vous allez voir. Dans votre POM, ajoutez la dépendance :
1<dependency>
2 <groupId>org.projectlombok</groupId>
3 <artifactId>lombok</artifactId>
4 <version>1.18.22</version>
5 <scope>provided</scope>
6</dependency>
C’est fait ! Cela suffit dans la plupart des cas.
Un possible piège #
Il y a une configuration supplémentaire dans deux cas :
- si vous utilisez JDK9+ avec un module-info.java ;
- ou si vous avez déjà configuré un processeur d’annotations2.
Si vous êtes dans cette situation, vous devez configurer la balise annotationProcessorPaths
du Maven Compiler Plugin.
Cela ressemble à ça :
1<annotationProcessorPaths>
2 <path>
3 <groupId>org.projectlombok</groupId>
4 <artifactId>lombok</artifactId>
5 <version>1.18.22</version>
6 </path>
7</annotationProcessorPaths>
C’est tout bon !
Une remarque sur le scope de la dépendance #
Vous avez peut-être remarqué que j’ai mis la dépendance dans le scope provided
.
En résumé, c’est une façon de dire à Maven : « Je n’ai pas besoin que cette dépendance soit incluse à l’exécution. »
Vous avez besoin des annotations lorsque vous compilez, mais elles sont éliminées de la classe.
Il est donc inutile qu’elles soient chargées sur le classpath puisqu’elles ne seront jamais appelées.
Si vous utilisez le fat jar de Spring Boot (et probablement d’autres mécanismes similaires), le scope provided
est ignoré.
Dans ce cas, vous pouvez vous appuyer sur la balise <optional>true</optional>
pour indiquer de ne pas embarquer la dépendance.
1<dependency>
2 <groupId>org.projectlombok</groupId>
3 <artifactId>lombok</artifactId>
4 <version>1.18.22</version>
5 <scope>provided</scope>
6 <optional>true</optional>
7</dependency>
Le plugin de l’IDE #
Votre IDE compile généralement vos classes à chaque fois que vous les modifiez, mais en s’appuyant généralement sur ses propres réglages et en se moquant bien des processeurs d’annotations, à moins d’installer les extensions qui vont bien. Avec Lombok, cela implique un éditeur qui va passer son temps à se plaindre qu’il ne connaît pas le getter que vous invoquez.
Lombok fournit les plugins, que vous utilisiez un IDE basé sur Eclipse, un produit IntelliJ, Netbeans ou un éditeur compatible avec Visual Studio Code. Ouvrez simplement la page ad hoc sur le site de Lombok (sous le menu « Install ») et découvrez la méthode d’installation pour votre outil.
Quelques exemples d’annotations Lombok #
Tout est en place. Par quoi commencer ?
Je propose de voir les basiques : les accesseurs.
Accesseurs #
Imaginons que nous écrivons un DTO pour laquelle nous n’avons pas envie d’écrire explicitement getters et setters.
Il nous suffit d’annoter la classe (ou les champs concernés) avec @Getter
and @Setter
, et le travail de développement est terminé !
Par défaut, les accesseurs seront public
, mais il est possible de surcharger ce réglage.
1import lombok.AccessLevel;
2import lombok.Getter;
3import lombok.Setter;
4
5@Getter
6@Setter
7public class Book {
8 private String title;
9 private String author;
10
11 @Getter(access = AccessLevel.NONE)
12 private String somePrivateFieldThatMustNotHaveAGetter;
13}
Bien moins verbeux que nos POJO habituels, non ?
Constructeurs #
Il vous faut un constructeur pour instancier votre classe. Lombok met à votre disposition trois annotations pour ce but :
@NoArgsConstructor
est explicite.@AllArgsConstructor
l’est aussi.@RequiredArgsConstructor
crée un constructeur avec un paramètre pour chaque champ qui doit être initialisé à l’instanciation (p.ex. les champsfinal
).
Comme c’est le cas pour les accesseurs, vous pouvez modifier la visibilité via le paramètre access
.
1import lombok.AccessLevel;
2import lombok.AllArgsConstructor;
3import lombok.Getter;
4import lombok.NoArgsConstructor;
5import lombok.Setter;
6
7@Getter
8@Setter
9@AllArgsConstructor
10@NoArgsConstructor(access = AccessLevel.PROTECTED)
11public class Book {
12 private String title;
13 private String author;
14}
Et là, le petit bonheur : vous n’avez plus besoin de mettre à jour votre constructeur à chaque fois que vous modifiez les champs. Tout ça se fera automatiquement !3
Builders #
Avec le temps, j’ai de plus en plus tendance à faire des objets immuables. Toutefois, l’initialisation d’un POJO immuable comportant de nombreux champs est pénible et le constructeur prenant tous les arguments perd souvent rapidement en clarté. Pour ces raisons, j’adore le pattern builder.
Et Lombok a une annotation pour ça aussi !
Je peux donc modifier ma classe Book
ainsi :
1import lombok.Builder;
2import lombok.Getter;
3
4@Builder
5@Getter
6public class Book {
7 private String title;
8 private String author;
9}
Et au lieu d’un new
, j’utiliserai le code suivant pour initialiser une instance :
1Book.builder()
2 .title("Roverandom")
3 .author("John Ronald Reuel Tolkien")
4 .build();
Si vous avez déjà écrit un builder vous-même, vous devez apprécier le temps économisé grâce à une petite annotation.
Bien, bien plus encore #
Nous arrêterons ici les exemples car je crains de vous ennuyer.
Sachez simplement que Lombok offre également des annotations pour générer vos méthodes equals()
et hashcode()
, vos toString()
, ou même ajouter un champ log
de votre framework de logging préféré.
Sachez aussi que vous pouvez modifier certains comportements par défaut via un fichier lombok.config
à placer à la racine de votre projet, juste à côté de votre pom.xml
.
Pour aller plus loin #
Bien entendu, vous pourrez trouver de nombreux tutoriels sur Lombok, mais je vous invite chaudement à regarder la documentation officielle. Elle est bien faite, chaque annotation est détaillée avec un exemple, le code Java « vanilla » équivalent, et les configurations possibles.
Et comme pour tout nouvel outil, un peu d’expérimentation vous aidera à le prendre plus rapidement en main.
Dans le pire des cas, l’impact sera sur la durée de compilation, mais à moins d’avoir un projet vraiment imposant et une utilisation massive de Lombok, je ne suis pas certain que vous puissiez faire la différence. ↩︎
Normalement, le Maven Compiler Plugin détecte les processeurs présents parmi les dépendances. Cependant, si des processeurs ont été manuellement configurés, cette détection est désactivée. ↩︎
Bien entendu, il faudra adapter les appels à ce constructeur, mais le compilateur sera capable de vous indiquer rapidement toutes les invocations maintenant invalides. ↩︎
Source
Project Lombok (EN)