hashCode, equals, toString, loggery, gettery, settery — kod, który niby inc nie robi, a jest potrzebny. Do tego może przysporzyć nam niemało problemów. A co byś powiedziała na to, żeby zastąpić go jedną adnotacją? Uwaga: po przeczytaniu tego wpisu nie będziesz chciała pracować w projektach bez Lombok’a ;)
Boilerplate
Być może spotkałaś się już z tym terminem, ogólnie oznacza on kod, który nie realizuje żadnej funkcji biznesowej, a jedynie służy połączeniu elementów w całości, udostępnieniu jakichś danych, logowaniu informacji itp — innymi słowy jest niezbędny, ale powtarzalny, nieciekawy do pisania i ‘zaśmiecający’ kod aplikacji.
Teminem tym określamy wszystkie metody takie jak gettery, settery, toString, hashCode oraz equals a także deklaracje loggerów i podobne. Wprawdzie wiele IDE pozwala nam generować ten kod automatycznie, to nadal musimy o nim pamiętać oraz ‘zaśmieca’ nam on resztę aplikacji.
Lombok
W odpowiedzi na te problemy powstał Lombok — stosunkowo niewielka biblioteka, która daje nam kilka adnotacji pozwalających na generowanie tego typu kodu automatycznie.
Co wyróżnia Lombok na tle innych narzędzi? O ile wcześniejsze rozwiązania opierały się o generyczny kod, najczęściej korzystający z refleksji w czasie działania programu, Lombok po prostu ‘dopisuje’ kod do aplikacji w czasie jej kompilacji. Dzięki temu nie tracimy na wydajności, a jednocześnie honoruje on wszystkie ‘nadpisane’ przez nas metody, dzięki czemu integruje się w sposób nieinwazyjny.
Co ciekawe, wykorzystuje ona nieudokumentowaną funkcjonalność kompilatory Javy — o ile po szczegóły zainteresowanych odsyłam do literatury (mówimy tutaj o modyfikacji AST), ogólnie chodzi o to, że w momencie kiedy Java ‘czyta’ kod przed kompilacją i zamienia go na reprezentację w pamięci, Lombok modyfikuje tą reprezentację dodając odpowiednie metody i pola. Dzięki temu dla Javy dodane funkcjonalności wyglądają jak standardowy kod, a jednocześnie nie mamy dodatkowych ograniczeń związanych z koniecznością dziedziczenia po jakiejś klasie lub odwoływania się do innych klas. Ponadto Lombok jest wymagany tylko w momencie kompilacji i nie musi być obecny w skompilowanej aplikacji.
Instalacja i użycie
Korzystanie z Lomboka wymaga jedynie jego dołączenia do projektu jako biblioteki — jeśli używamy Mavena, wystarczy skorzystać z poniższego fragmentu:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
Pozostaje jeszcze jeden krok — ‘powiedzenie’ naszemu IDE, aby widział także metody generowane przez Lomboka. Na szczęście sam project Lombok się tym zajmie w przypadku Eclipse — wystarczy uruchomić plik JAR i postępować wg wskazówek na ekranie. W przypadku IntelliJ dostępny jest plugin, który możemy zainstalować w standardowy sposób z poziomu IntelliJ.
Adnotacje
Lombok oferuje funkcjonalność za pośrednictwem adnotacji — zarówno na poziomie klas jak i metod. Poniżej zbiór najważniejszych, pełną dokumentację możesz znaleźć na stronie projektu pod adresem https://projectlombok.org/features/index.html .
@Getter / @Setter
Adnotacje te pozwalają na dodanie getterów lub setterów dla wybranych pól. Adnotacja umieszczona nad klasą spowoduje dodanie metod dla wszystkich pól tej klasy. Przykładowe zastosowanie:
@Getter
@Setter
public class Aplikacja {
@Setter(AccessLevel.PROTECTED) private String prywatnePole;
private String publicznePole;
}
Powyższy kod utworzy publiczne gettery i settery do obu pól, za wyjątkiem metody setPrywatnePole, która będzie miała widoczność protected.
@EqualsAndHashCode
Dzięki tej adnotacji, wygenerowane zostaną metody hashCode oraz equals uwzględniające wszystkie pola. Metody te oczywiście spełniają kontrakt hashCode-equals, o którym pisaliśmy więcej w osobnym wpisie. Przykładowe użycie:
@EqualsAndHashCode
public class Aplikacja {
private String prywatnePole;
private String publicznePole;
}
Powyższy kod utworzy metody hashCode oraz equals, które nadpisują te domyślne, z klasy Object.
@RequiredArgsConstructor / @AllArgsConstructor / @NoArgsConstructor
Generuje konstruktor dla klasy, który przyjmuje jako argumenty odpowiednio: pola wymagane (prywatne i finalne), wszystkie pola, nie przyjmuje argumentów. Przykładowe użycie:
@AllArgsConstructor
public class Aplikacja {
private String prywatnePole;
private String publicznePole;
}
Ta adnotacja utworzy konstruktor przyjmujący dwa argumenty typu String, które zostaną przypisane do pól.
@Data
Adnotacja ta łączy w sobie kilka innych: @Getter, @Setter, @HashCodeAndEquals, @RequiredArgsConstructor oraz @ToString. Zapis:
@Data
public class Aplikacja {
private String prywatnePole;
private String publicznePole;
}
Jest więc równoważny z poniższym:
@Getter
@Setter
@HashCodeAndEquals
@RequiredArgsConstructor
@ToString
public class Aplikacja {
private String prywatnePole;
private String publicznePole;
}
@Log i podobne
Ta adnotacja pozwala dodać logger, jako pole o nazwie log. Do wyboru mamy najpopularniejsze biblioteki:
@Log4j2 — Log4j w wersji 2
@Slf4j — Slf4J
@CommonsLog — Apache Commons Logging
Uwaga — adnotacje te nie dodają odpowiednich bibliotek! Musisz sama dodać zależności do tych bibliotek.
Pozostałe adnotacje
Lombok oferuje także kilka innych, użytecznych adnotacji. @Builder czy @Synchronized pozwalają na uproszczenie kodu w specyficznych sytuacjach, ale ich zastosowanie jest bardziej specyficzne. Po szczegółowy opis każdej z nich odsyłamy do dokumentacji pod adresem https://projectlombok.org/features/index.html
Podsumowanie
Lombok jest naprawdę świetnym rozwiązaniem na ‘boilerplate code’ — wg nas zwiększa czytelność kodu i poprawia sensowność metryk takich jak np. pokrycie kodu. Zanim jednak użyjesz go w projekcie w pracy, upewnij się, że zespół nie ma nic przeciwko! Jeśli jednak do tej pory nie używaliście w zespole — może warto, żebyś zaproponowała?