Niet alleen is Lombok een Indonesisch eiland, maar tegelijk is Lombok een project dat mogelijkheden toevoegt aan de Java compiler. Hierdoor heb je als ontwikkelaar extra features tot je beschikking die niet standaard in Java zitten. Het doel van Lombok is vooral om het schrijven of genereren van zogenaamde boiler plate code, die helaas in Java vaak nodig is, overbodig te maken. Door je klasse van Lombok annotaties te voorzien, vertel je de compiler deze code – build time – voor je te genereren.
Het logo van Project Lombok is de Lombok peper, goed passend bij de slogan van de makers: “Spice up your java”.
Een aantal handige features uit Lombok
Feature | Omschrijving |
val | Hiermee kun je lokale variabelen zonder type-aanduiding definiëren. Het type zal worden afgeleid. |
@NonNull | Automatisch gegenereerde null check op veld of parameter |
@Getter, @Setter |
De naam zegt het al, deze genereren getter en setter methoden |
@ToString | Gegenereerde toString() |
@EqualsAndHashCode | Gegenereerde equals() en hashCode() methoden. Deze nemen vaak veel plek in en als je een veld aan je klasse toevoegt, moet je niet vergeten ze eventueel aan te passen. |
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor |
Gegenereerde constructors |
@Builder | Genereert een builder klasse voor het aanmaken van instanties. Voorkomt het telescoping constructor probleem. |
@Log | Zet een logger met de naam log in je klasse. Hiermee heb je dus nooit meer last van een copy paste fout met een logger voor de verkeerde klasse. |
@Data | Een samenvoeging van @ToString, @EqualsAndHashCode, @Getter, @Setter en @RequiredArgsConstructor. Zo staat je POJO niet zo vol met annotaties. |
Kijk voor de volledige lijst, en ook uitgebreide code voorbeelden, op de site van Lombok. De meeste van de functies zijn te sturen door middel van parameters op de annotatie. Zo kun je de automatisch gegenereerde functionaliteit helemaal aanpassen aan je wensen. Bijvoorbeeld een toString() met of zonder veldnamen:
@ToString(includeFieldNames=false)
Installatie en gebruik
De makers van lombok hebben installatie in Eclipse vrij eenvoudig gemaakt. Je downloadt lombok.jar van de website (zie links verderop in het artikel) en voert met een dubbelklik deze executable jar uit. De installer zal vervolgens op zoek gaan naar je Eclipse installatie en de jar op de juiste plek zetten. Het kan ook met de hand. Kopieer de jar naar de eclipse installatie directory en voeg je de volgende regel aan je eclipse.ini toe:
-javaagent:[pad naar eclipse installatie]/lombok.jar
Installatiescherm
In NetBeans IDE werkt Lombok out of the box. Dependency toevoegen en gaan met die peper.
Voor IntelliJ IDEA is een Lombok plugin beschikbaar. Deze is door iemand anders ontwikkeld, maar wordt regelmatig bijgewerkt. (zie links verderop in het artikel).
De volgende stap is Lombok als dependency toevoegen. In Maven gaat dat als volgt:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
Als het goed is, heb je zonder veel moeite je ontwikkelomgeving Lombok ready. Laten we snel gaan kijken hoe je dit in de praktijk toepast.
Plain Java
public class Klant {
private String voornaam;
private String tussenvoegsel;
private String achternaam;
public String getVoornaam() {
return this.voornaam;
}
public void setVoornaam(final String voornaam) {
if (voornaam == null) {
throw new java.lang.NullPointerException("voornaam");
}
this.voornaam = voornaam;
}
public String getTussenvoegsel() {
return this.tussenvoegsel;
}
public void setTussenvoegsel(final String tussenvoegsel) {
this.tussenvoegsel = tussenvoegsel;
}
public String getAchternaam() {
return this.achternaam;
}
public void setAchternaam(final String achternaam) {
if (achternaam == null) {
throw new java.lang.NullPointerException("achternaam");
}
this.achternaam = achternaam;
}
}
Java met Lombok
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
public class LombokKlant {
@NonNull
@Getter
@Setter
private String voornaam;
@Getter
@Setter
private String tussenvoegsel;
@NonNull
@Getter
@Setter
private String achternaam;
}
Listing: Voorbeeld code, getters, setters en null check
Getters en Setters
In het eerste voorbeeld zien we de @Gettter, @Setter en @NonNull annotaties in actie. De getter en setter methoden worden nu achter de schermen voor je gegenereerd tijdens compilatie. De bytecode is uiteindelijk dus echt anders dan de java code die we hier zien. In je IDE zal je dit in de outline terug kunnen zien.
Screenshot outline
Builder
Het is een goede gewoonte om geen setters aan te bieden als dat niet nodig is. Zo creëer je een correct afgeschermd object. Voor objecten met veel (optionele) waarden kun je dan al gauw tegen het telescoping constructor probleem aanlopen. Dat wil zeggen, een heleboel verschillende constructors met allemaal een argument meer. Een builder is hier een goed alternatief voor, zie ook het hoofdstuk “ Creating and Destroying Java Objects” uit “Effective Java” door Joshua Blog, ook online te vinden. (zie link verderop in het artikel)
Het schrijven van een builder is alleen relatief veel werk en levert het veel code. Gelukkig heeft
Lombok hier een handige oplossing voor. We halen de @Setter annotaties weg en voegen een @Builder annotatie toe.
Plain Java
Klant(final String voornaam, final String tussenvoegsel, final String achternaam) {
if (voornaam == null) {
throw new java.lang.NullPointerException("voornaam");
}
if (achternaam == null) {
throw new java.lang.NullPointerException("achternaam");
}
this.voornaam = voornaam;
this.tussenvoegsel = tussenvoegsel;
this.achternaam = achternaam;
}
public static class KlantBuilder {
private String voornaam;
private String tussenvoegsel;
private String achternaam;
KlantBuilder() {
}
public KlantBuilder voornaam(final String voornaam) {
this.voornaam = voornaam;
return this;
}
public KlantBuilder tussenvoegsel(final String tussenvoegsel) {
this.tussenvoegsel = tussenvoegsel;
return this;
}
public KlantBuilder achternaam(final String achternaam) {
this.achternaam = achternaam;
return this;
}
public Klant build() {
return new Klant(voornaam, tussenvoegsel, achternaam);
}
public java.lang.String toString() {
return "Klant.KlantBuilder(voornaam=" + this.voornaam + ", tussenvoegsel=" + this.tussenvoegsel
+ ", achternaam=" + this.achternaam + ")";
}
}
public static KlantBuilder builder() {
return new KlantBuilder();
}
Java met Lombok
import lombok.Builder;
@Builder
public class LombokKlant {
Listing: Voorbeeld code uitbreiding, builder met drie waarden in Java al heel veel code, met Lombok een enkele annotatie
Hoe werkt het?
Het gaat te ver om de volledige werking van Lombok te beschrijven. Hier volgt daarom een iets versimpeld beeld. Lombok maakt onder andere gebruik van de mogelijkheid van JSR-269 om annotaties in het compile proces te gebruiken. (link JSR-269)
De compiler parset de java code en bouwt een Abstract Syntax Tree(AST) op. Dit is een boomstructuur van code elementen. (Zie AST link) Eclipse heeft zijn eigen compiler (EJC) en zijn eigen AST implementatie. Dit betekent gedeeltelijk dubbel werk voor de Lombok ontwikkelaar.
Lombok bestaat uit een aantal onderdelen:
- Een verzameling annotaties (@Getter, @Setter, etc.) Deze hebben we hierboven al gezien en dit zijn eigenlijk de enige elementen die je in je eigen code zal gebruiken.
- Interne Lombok code opgebouwd uit onder andere:
- Een Annotation Processor volgens JSR-269. JSR-269 biedt een plugging mechanisme om build-time annotaties te processen. Lombok is op deze manier in staat als onderdeel van het compilatie proces te werken.
- Code om met de javac AST en de eclipse AST om te gaan. Hierbij is lombok afhankelijk van interne APIs.
- Twee sets Handlers. Voor iedere annotatie een Handler voor javac en één voor EJC. De Handers zijn verantwoordelijk voor het toevoegen van de functionaliteit achter de annotaties. Ze kunnen de AST manipuleren en zo elementen (zoals bijvoorbeeld een nieuwe getter methode) aan de AST toevoegen.
Waarom Lombok?
- Leuker – Ontwikkelaars zijn over het algemeen lui, als iets te automatiseren valt, zullen ze het doen. Boiler plate code schrijven gaat immers snel vervelen.
- Sneller – Doordat je minder code hoeft de schrijven, heb je sneller resultaat
- Minder fouten – Bij alle code die je schrijft, is er een kans dat je een fout maakt, maar code die je niet schrijft, kan ook geen fouten bevatten.
- Overzichtelijker – Een klasse met wat waarden en getters en setter beslaat al gauw een aantal pagina’s. Dezelfde klasse met lombok annotaties is heel wat kleiner en overzichtelijker.
Waarom geen Lombok?
- Getters en setters kun je ook prima genereren vanuit je IDE.
- Annotaties lezen minder fijn dan code. Een klasse die uit meer annotaties bestaat dan regels code is een ramp om te lezen. (Ter verdediging: De annotaties van Lombok vrij duidelijk qua naamgeving. Daarnaast kent Lombok een tweetal annotaties die veel gebruikte andere annotaties omvatten. @Data en @Value.)
- Bij het upgraden van Java versie of IDE ben je afhankelijk van compatibiliteit van Lombok met deze nieuwe versie. Zoals eerder gezegd, gebruikt Lombok interne functies van de JVM en van Eclipse. Het kan gebeuren dat je applicatie niet meer werkt na een upgrade. Dan kun je drie dingen doen:
- Alsnog alle code schrijven die Lombok achter de schermen genereerde. Dit hoef je overigens niet zelf te doen. Lombok biedt de mogelijkheid deze code voor je te generen.
- Terug naar de oude versie en wachten op de lombok ontwikkelaars totdat je nieuwe versie ook ondersteund wordt.
- Zelf het probleem proberen op te lossen in Lombok, aangezien Lombok een open source project is.
- Refactoring is niet altijd ongevaarlijk. Bepaalde onschuldig lijkende refactorings kunnen vervelende bijwerkingen hebben. Zo kan de volgorde van de parameters van een gegenereerde Constructor wijzigen als je de volgorde van de velden in de klasse aanpast. Als deze van hetzelfde type zijn, dan kan dit ongemerkt mis gaan.
- Regelnummers in debug mode kunnen vreemd zijn. De actieve regel die je in debug ziet, bevat niet altijd code. Wel wordt keurig de juiste annotatie aangewezen als je iedere annotatie op een aparte regel zet.
Alternatieven?
De meeste Java ontwikkelaars maken al veel gebruik van code generatie tooling in de IDE. Voordeel ten opzichte van Lombok is de leesbaarheid, maar tot op een zekere hoogte. Nadelen zijn dat een aantal handige annotaties van Lombok geen vervanger kent. De klassen zijn groter. En pas op met wijzigingen, want dan is opnieuw genereren nodig.
Een andere opties die je hebt, is overstappen naar een andere taal, bijvoorbeeld groovy, of een geheel ander platform, bijvoorbeeld C#. Allen bieden een aantal van de bovenstaande verfijningen out of the box. Daarbij heeft groovy voor Java gebruikers de beste papieren, aangezien je deze gewoon op de JVM kunt draaien en kunt mixen met Java klassen. Of je kunt heel lang gaan wachten op een nieuwe Java versie en hopen dat hierin soortgelijke aanpassingen zijn verwerkt.
Meer mogelijkheden
Als je Lombok gebruikt en er tevreden over bent, maak een fork en breid het uit. Je zou andere stukken veel voorkomende code kunnen nemen en deze vervangen door zelf geschreven lombok annotaties. Naast de annotatie schrijf je nog twee klassen, een Handler voor javac en een Handler voor EJC. Voor een uitgebreid voorbeeld, zie het artikel op IBM DeveloperWorks. (link)
Conclusie
In dit artikel heb je een indruk gekregen van wat er mogelijk is met Lombok. Of je binnen je team of project gebruik van Lombok wilt maken, hangt van een aantal factoren af. Maak een goede afweging tussen de voordelen en nadelen in jouw situatie, probeer het eens uit en misschien zeg je ook wel gedag tegen een grote hoeveelheid standaard code die alleen maar voor ballast zorgt.
Handige links