Lombok – jak pozbyć się boilerplate code w Java [lombok data, value, builder, maven]

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 – project Lombok.

Lombok Getter/Setter

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

@Getter
@Setter
public class UserGS {

	private String name;

	@Setter(AccessLevel.PROTECTED)
	private int age;
}

Powyższy kod wygeneruje:

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

Lombok 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ę.

@EqualsAndHashCode(callSuper = false, exclude = {})
public class UserEqualsAndHashCode {
	private String name;
	private Integer age;
}

Lombok 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.

Lombok RequiredArgsConstructor

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

@RequiredArgsConstructor
public class UserRequiredArgsConstructor {

    private final String name;

    private int age;
}

Lombok Data

Adnotacja Lombok @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.

@Data
public class UserLombokData {
	private final String name;
	private Integer age;
}
@Getter
@Setter
@EqualsAndHashCode
@RequiredArgsConstructor
@ToString
public class UserDataEquivalent {

	private final String name;
	private int age;
}

Lombok 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.

@Test
public void localVariableTest() {
	val list = Arrays.asList("item");
	String listElement = list.get(0);

	Assert.assertEquals("item", listElement);
}

@Test
public void foreachTest() {
	val list = new ArrayList<>();
	for (val item : Arrays.asList("item1", "item2")) {
		list.add(item);
	}

	Assert.assertEquals(Arrays.asList("item1", "item2"), list);
}

Info EDIT: Od wersji 10, Java oferuje nową konstrukcję var, która działa bardzo podobnie. Dobre jest to, że standard powoli przejmuje sprawdzające się pomysły z zewnętrznych bibliotek.

Lombok Cleanup

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

Lombok Value

Adnotacja Lombok @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 (ponieważ są to obiekty immutable/niezmienne)
@Value
public class UserValue {

	private String name;
	private int age;
} 

Lombok 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

Lombok 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.

Lombok 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ę
  • 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.
public class SneakyThrowsExample {

	@Test(expected = Exception.class)
	@SneakyThrows
	public void throwsExcpeptionTest() {
		throw new Exception();
	}

	@Test
	@SneakyThrows(UnsupportedEncodingException.class)
	public void utf8ToString() {
		String str = new String("".getBytes(), "UTF-8");

		Assert.assertEquals("", str);
	}
}

Lombok Log, Lombok Log4j, Slf4j

@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());

@Log
public class LogExample {

	@Test
	public void infoTest() {
		log.log(Level.INFO, "Info message");
	}
}

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

Info 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ę.

Uwaga 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 – Lombok Maven

Instalacja jest bardzo prosta, wystarczy dodać jednego jar’ka do projektu. W przypadku maven’a będzie to dodanie zależności do pliku pom.xml. Nic jednak nie stoi na przeszkodzie, żeby zrobić, to korzystając z innego rozwiązania do zarządzania zależnościami np. z Gradle.

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

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

Podsumowanie – Lombok Java

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ć – w tej kwestii musimy liczyć na wsparcie naszego IDE, np. Intellij Idea oferuje takie wsparcie.

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.


20+ BONUSOWYCH materiałów z programowania

e-book – „8 rzeczy, które musisz wiedzieć, żeby dostać pracę jako programista”,
e-book – „Java Cheat Sheet”,
checklista – „Pytania rekrutacyjne”
i wiele, wiele wiecej!

Jak zostać programistą

3 komentarze
Share:

3 Comments

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *