В предыдущей статье мы рассмотрели, подключить библиотеку Lombok к проекту и как установить плагин в IDE. Сегодня же мы расскажем, как использовать Lombok для уменьшения шаблонного кода при разработке программ.

Как использовать Lombok

При написании кода используются аннотации Lombok. С помощью аннатоций Lombok на этапе компиляции генерирует шаблонный код. С помощью аннотаций Lombok можно генерировать конструкторы классов, геттеры и сеттеры, методы toString, equals и hashCode. Давайте вкратце рассмотрим использование Lombok в работе.

Сеттеры и геттеры

Для генерации сеттеров и геттеров используются аннотации @Setter и @Getter, соответственно. Эти аннотации удобно навесить на POJO класс со множеством полей, дабы не генерировать шаблонный код самому.

@Getter
@Setter
public class Person {
    private String firstName;
    private String secondName;
    private Address address;
}

Здесь мы объявили класс Person с тремя полями и указали аннотации @Getter и @Setter. Если вы правильно подключили Lombok и установили плагин, то он сразу же создаст геттеры и сеттеры. Теперь можно воспользоваться созданными методами:

Person person = new Person();
person.setFirstName("Nicola");
person.setSecondName("Tesla");
person.setAddress(new Address());

System.out.println(person.getFirstName() + " " + person.getSecondName());

Примечание: если вы определите какой-либо геттер или сеттер в классе, то Lombok не будет его трогать при генерации кода. То есть уже определённые методы будут иметь приоритет над вновь создаваемыми. Также для нестатических final полей не будут сгенерированы сеттеры.

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Person {
    private String firstName;
    private String secondName;
    private Address address;
}
public class Address {
}
public class LombokGetterSetter {
    public static void main(String[] args) {
        Person person = new Person();
        person.setFirstName("Nicola");
        person.setSecondName("Tesla");
        person.setAddress(new Address());

        System.out.println(person.getFirstName() + " " + person.getSecondName());
    }
}

Конструкторы

Рутинное написание типовых конструкторов можно поручить библиотеке Lombok, аннотировав нужный класс с помощью аннотаций @AllArgsConstructor, @RequiredArgsConstructor и @NoArgsConstructor. Как и следует из названий, эти аннотации приведут к генерированию таких конструкторов:

  • @AllArgsConstructor – консктруктор, использующий все поля класса
  • @RequiredArgsConstructor – конструктор, использующий все final поля класса
  • @NoArgsConstructor – конструктор без параметров

Эти аннотации можно комбинировать между собой и использовать вместе с аннотациями @Getter и @Setter.

Давайте определим класс Person с аннотациями @NoArgsConstructor и @AllArgsConstructor:

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String firstName;
    private String secondName;
    private Address address;
}
public class ConstructorLombok {
    public static void main(String[] args) {
        Person a = new Person();
        a.setFirstName("Alice");
        System.out.println(a.getFirstName());

        Person b = new Person("Bob", "Marley", new Address());
        System.out.println(b.getFirstName());
    }
}

Как вы видите, Lombok сгенерировал конструктор без параметров и конструктор со всеми параметрами.

Примечание: при использовании в классе нестатических final полей невозможно использовать аннотацию @NoArgsConstructor, так как в конструкторе без параметров такие поля не будут инициализированы.

@Getter
@Setter
@NoArgsConstructor // эту аннотацию использовать нельзя
@RequiredArgsConstructor
@AllArgsConstructor
public class Person {
    private final String firstName;
    private String secondName;
    private Address address;
}

Методы toString, equals, и hashCode

Для создания метода toString воспользуйтесь аннотацией @ToString:

@Getter
@Setter
@ToString
public class Person {
    private String firstName;
    private String secondName;
    private Address address;
}
public class ToStringLombok {
    public static void main(String[] args) {
        Person a = new Person();
        a.setFirstName("Nicola");
        a.setSecondName("Tesla");
        System.out.println(a);
    }
}

Выполнение данного кода выведет в консоль следующий текст:

Person(firstName=Nicola, secondName=Tesla, address=null)

Методы equals и hashCode генерируются с помощью аннотации @EqualsAndHashCode. Для простых классов вполне ясно и понятно использовать генерирование этих методов, но в нетривиальных случаях рекомендуется создавать equals и hashCode вручную (или средствами IDE).

Все методы воедино

Зачастую аннотации @Getter, @Setter, @ToString, @EqualsAndHashCode, и @RequiredArgsConstructor используются вместе для одного класса. Для такого типового подхода существует аннотация @Data, объединяющая все перечисленные аннотации.

С помощью аннотации @Data будут сгенерированы геттеры и сеттеры, конструктор со всеми final полями, методы toString, equals и hashCode. Эти два класса равнозначны:

@RequiredArgsConstructor
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class PersonA {
    private String firstName;
    private String secondName;
    private Address address;
}
@Data
public class PersonB {
    private String firstName;
    private String secondName;
    private Address address;
}

Method chaining

Предположим, у нас есть класс с множеством полей. Использовать конструктор со всеми аргументами – не вариант, получится слишком длинный список аргументов. Вызывать по одному сеттеру в каждой строке может быть тоже непрактично. В этом случае можно воспользоваться аннотацией @Builder на нужном классе и использовать любой набор полей для построения объекта:

public class UserLombokExample {

    public static void main(String[] args) {
        User user = User.builder()
                .person(new Person())
                .id(1337L)
                .username("alex")
                .build();

        System.out.println(user);
    }
}
@Builder
@ToString
public class User {
    private final Long id;
    private String username;
    private char[] password;
    private Person person;
    private List<String> actions;
}

Начните построение объекта с вызова метода builder(), затем используйте нужные сеттеры, после чего вызовите метод build() для построения объекта.

Логгеры

Библиотеку Lombok можно использовать не только для моделей и POJO, но и для других классов – например, сервисов. Если в классе используется логгер, то генерирование этого поля можно поручить Lombok.

Например, вместо данного кода:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogLombok {
    private static final Logger LOG = LoggerFactory.getLogger(LogLombok.class);

    public static void main(String[] args) {
        LOG.info("Just test");
    }
}

Можно написать следующий код, используя @Slf4j:

import lombok.extern.java.Log;

@Slf4j
public class LogLombok2 {
    public static void main(String[] args) {
        log.info("Just test");
    }
}

В аннотированном классе будет создано такое final поле с именем log.

Помимо @Slf4j, в Lombok есть ещё аннотации @Log и @CommonsLog. Эти аннотации отвечают за генерацию переменных следующих типов:

  • @Log – java.util.logging.Logger
  • @Slf4j – org.slf4j.Logger
  • @CommonsLog – org.apache.commons.logging.Log

NonNull

Если пометить поле аннотацией @NonNull, то в случае, если будет попытка присвоить данному полю значение null, Lombok выбросит исключение:

import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class StrictUser {
    
    @NonNull
    private final Long id;
    
    private String username;
}
public class NonNullLombok {
    public static void main(String[] args) {
        StrictUser user = new StrictUser(null); // NullPointerException
    }
}

Такое поведение будет аналогично тому, если бы мы написали в начале конструктора или сеттера проверку на null:

public StrictUser(@NonNull Long id) {
    Objects.requireNonNull(id);
    this.id = id;
}

Переменные @var и константы @val

В Lombok есть интересные аннотации @var для обозначения «изменяемых» переменных и @val для констант. Обе эти аннотации применяются для обозначения локальных переменных.

Начиная с Java 10, синтаксис языка позволяет делать то же самое. Можно использовать val для объявления локальной константы и var для объявления локальной переменной. Особенностью данных двух ключевых слов является то, что тип переменной (или константы) будет определён компилятором автоматически. То есть не потребуется указывать тип переменной (константы) вручную:

import lombok.val;
import lombok.var;

public class ValVarLombok {
    public static void main(String[] args) {
        val pi = 3.1415926;
        var speed = 100;

        pi = 4; // ошибка компиляции
        speed = 240;
    }
}

Данные аннотации можно использовать и в «классическом» стиле:

import lombok.val;
import lombok.var;

public class ValVarLombok2 {
    public static void main(String[] args) {
        @val double pi = 3.1415926;
        @var double speed = 100;

        System.out.println("pi=" + pi);
        System.out.println("speed=" + speed);

        pi = 4;
        speed = 240;

        System.out.println("pi=" + pi);
        System.out.println("speed=" + speed);
    }
}

В этом случае ошибки компиляции не будет, но значение константы не изменится при попытке её переопределить.

Заключение

В данной статье мы рассказали о ключевых возможностях библиотеки Lombok. С помощью Lombok вы можете упростить и ускорить ращработку программ, используя аннотации длягенерирования конструкторов (@AllArgsConstructor, @RequiredArgsConstructor и @NoArgsConstructor), геттеров и сеттеров (@Getter, @Setter), генерировать стандартные методы (@ToString, @EqualsAndHashCode), генерировать поле для логгера (@Log, @Slf4j, @CommonsLog), использовать функциональность val и var в версиях ниже Java 10. Дерзайте!

Как использовать библиотеку Lombok