Lombok – jak pozbyć się getterów i setterów z kodu

Lombok

Lombok to stosunkowo niewielka biblioteka, rozwijana na zasadach wolnej licencji (MIT license). Najbardziej jest znana z automatycznego generowania getterów i setterów, jednak jej możliwości są dużo większe. Głównym celem jej powstania jest chęć poprawy niedogodności językowych Javy. Biblioteka udostępnia zbiór adnotacji pozwalających na automatyczne generowanie kodu, w celu ograniczenia kłopotliwego boilerplate code.

Funkcjonalności Lombok

Funkcjonalności udostępnione są z wykorzystaniem adnotacji na poziomie klas i metod, pełna ich lista dostępna jest na stronie projektu.

@Getter/@Setter

Adnotacje dodają gettery oraz settery dla wybranych pól lub dla wszystkich, jeżeli zostały dodane na poziomie klasy.

Powyższy kod wygeneruje:

  • publiczne gettery dla obu pól
  • chroniony (protected) setter dla pola age
  • publiczny dla pola name.

@EqualsAndHashCode

Generuje jednocześnie metody equals i hashCode spełniając kontrakt między nimi.
Domyślnie brane są pod uwagę wszystkie pola, które nie są oznaczone jako statyczne i transient.
Korzystając z opcjonalnej konfiguracji można wykluczyć niektóre pola lub zdefiniować, czy pola z klasy rodzica również mają być brane pod uwagę.

@ToString

Generuje metodę toString() z wszystkich pól, które nie są oznaczone jako statyczne lub transient.
Tu również można dokonfigurować zachowanie przez parametry adnotacji: exclude i callSuper.

Domyślnie wyświetlany format to pary: name:value,

UserToString(name=null, age=0)

ale można wyłączyć nazwy pól przez parametr includeFieldNames.

@RequiredArgsConstructor

Ta adnotacja wygeneruje konstruktor z wszystkimi wymaganymi polami. W poniższym przykładzie będzie to tylko pole name.

@Data

@Data to najbardziej popularna adnotacja, łączy w sobie kilka innych: @Getter, @Setter, @EqualsAndHashCode, @RequiredArgsConstructor oraz @ToString, dlatego poniższe przykłady generują równoważny kod.

val

Val to nowa konstrukcja dla programistów Javy, bardziej jest znana chociażby w Scali. Dzięki niej nie trzeba deklarować typu zmiennej, a kompilator sam go wywnioskuje na podstawie przypisanej do niej wartości. Ponieważ ten mechanizm działa już podczas kompilacji, wymagane jest zainicjowanie zmiennej przy jej deklaracji.

To rozwiązanie ma również swoje ograniczenia. Działa tylko dla zmiennych lokalnych oraz w pętlach foreach. Występują również kłopoty z podpowiadaniem składni dla takich zmiennych w IDE.

@Cleanup

Celem tej adnotacji jest automatyczna obsługa zamykania zasobów. Od momentu wprowadzenia w Javie try with resources straciła na znaczeniu.

@Value

@Value to odpowiednik @Data dla niemodyfikowalnych obiektów (immutable objects).

Dla tej adnotacji kompilator wygeneruje:

  • metodę toString
  • hashCode oraz equals
  • konstruktor dla wszystkich niezainicjowanych pól
  • oznaczy pola jako final
  • wygeneruje również gettery, ale BEZ setterów

@Builder

W celu implementacji wzorca projektowego builder wystarczy adnotować główną klasę adnotacją @Builder, a kompilator wygeneruje statyczną zagnieżdżoną klasę buildera oraz niezbędny konstruktor w głównej klasie. To podejście bardzo dobrze współgra z adnotacją @Value. Dzięki takiej kombinacji uzyskujemy w pełni funkcjonalny value object z obsługującym go builderem.

Lombok builder

Lombok builder

@Synchronized

Jest to alternatywa dla synchronizowanych metod. Domyślne synchronizowane metody zakładają blokadę na obiekcie this (lub metody statyczne na obiekcie klasy), natomiast @Synchronized generuje prywatną zmienną, którą wykorzystuje w tym celu. Daje to trochę większe możliwości, jednak osobiście preferowałbym standardowe rozwiązania z pakietu: java.util.concurrent.

@SneakyThrows

@SneakyThrows pozwala uniknąć deklarowania w definicji metod wyjątków oznaczonych (checked exceptions). To dość ryzykowny mechanizm, dlatego zalecam ostrożność przy jego wykorzystywaniu.

Można wyróżnić dwie główne sytuacje, kiedy takie rozwiązanie będzie pomocne:

  • gdy mamy pewność, że  dany wyjątek nigdy nie poleci i nie chcemy dodatkowo zaciemniać kodu. Jak w przykładzie z metodą utf8ToString, gdzie wiemy, że kodowanie UTF-8 jest zawsze wspierane przez Javę
  • gdy wyjątki są przechwytywane i obsługiwane w dalszych warstwach aplikacji. Alternatywnym rozwiązaniem jest przechwycenie takiego wyjątku i ponowne rzucenie go opakowując wcześniej w RuntimeException.

@Log

@Log jest to wsparcie dla loggerów. Poniższy kod wygeneruje w locie prywatną zmienną w klasie:

private static final java.util.logging.Logger log =
java.util.logging.Logger.getLogger(LogExample.class.getName());

Lombok umożliwia również skorzystanie z innych bibliotek do logowanie przed adnotacje: @CommonsLog, @JBossLog, @Log4j, @Log4j2, @Slf4j oraz @XSlf4j.

Uwaga – adnotacje te nie dodają odpowiednich bibliotek! Musisz samodzielnie dodać dla nich wszystkie niezbędne zależności.

Lombok - jak to działa?

Lombok – jak to działa?

Jak to działa pod maską?

Lombok niejako dopisuje kod do aplikacji podczas jej kompilacji. Kiedy Java odczytuje kod źródłowy przed właściwą kompilacją i zamienia go na reprezentację w pamięci, Lombok modyfikuje ją, dodając odpowiednie pola i metody.

Dzięki temu nie ma potrzeby załączenia biblioteki do finalnej aplikacji podczas jej działania. Nie ma również dodatkowych narzutów na wydajność, związanych np. z korzystaniem z refleksji, czy narzuconego dziedziczenia po konkretnej klasie, jak robią to inne frameworki. Taki kod zachowuje się jak każdy inny, napisany ręcznie przez programistę.

Niestety w swoim działaniu opiera się na wewnętrznych, nieudokumentowanych funkcjonalnościach Javy, co może potencjalnie powodować problemy w przyszłości.

Lombok UserData

Lombok UserData

Instalacja Lombok

Instalacja jest bardzo prosta, wystarczy dodać jednego jar’ka do projektu. W przypadku maven’a będzie to dodanie do pliku pom.xml

Wystarczy scope: provided, ponieważ Lombok jest potrzebny tylko przy kompilacji.

Podsumowanie

Lombok jest niewątpliwie biblioteką, której warto się przyjrzeć, chociażby z racji swojej popularności i oferowanych funkcjonalności.

Przed jej wykorzystaniem produkcyjnie trochę odstrasza fakt wykorzystania wewnętrznego api JDK. Jednak, jak do tej pory, tylko raz miałem kłopoty z tą biblioteką i wystąpiły one już na etapie budowania projektu (inna biblioteka również próbowała modyfikować bytecode).

Pewnym utrudnieniem jest również brak fizycznego kodu. Z powodu braku możliwości podejrzenia kodu czasem trudniej jest zrozumieć jego działanie oraz trudniej jest go debugować.

Jednak mimo powyższych niedogodności komfort korzystania z tej biblioteki jest bardzo duży. Osobiście korzystam głównie z generowania metod: toString, equals oraz hashCode i widzę, jak bardzo to przydaje się w projekcie.

Programista – Pytania rekrutacyjne

Lista pytań rekrutacyjnych, które pozwolą przygotować Ci się na rozmowę kwalifikacyjną.

No comments
Share:

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *