Java for Beginner
675 subscribers
560 photos
156 videos
12 files
858 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Введение в Lombok и базовые аннотации

Всем привет!

Сегодня мы начинаем серию постов, посвященных библиотеке Lombok. Если вы устали писать boilerplate-код (геттеры, сеттеры, toString, equals и т.д.), то Lombok — это то, что вам нужно! Давайте разберемся, что это за библиотека и как она может упростить вашу жизнь.


Что такое Lombok и зачем он нужен?

Lombok — это библиотека для Java, которая помогает сократить объем шаблонного кода. Она автоматически генерирует методы, такие как геттеры, сеттеры, toString(), equals(), hashCode() и другие, во время компиляции. Это делает код более чистым и удобным для чтения.

Пример без Lombok:
public class User {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


Пример с Lombok:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
private String name;
private int age;
}
Lombok убирает необходимость писать шаблонный код, что особенно полезно в больших проектах.



Установка Lombok в проект

Maven (проверяйте версии в библиотеке Maven):

Добавьте зависимость в pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>


Gradle:
Добавьте зависимость в build.gradle:
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.36'
annotationProcessor 'org.projectlombok:lombok:1.18.36'
}


Аннотация @Getter и @Setter

Эти аннотации автоматически генерируют геттеры и сеттеры для всех полей класса.


Пример:
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует методы getName(), setName(), getAge(), setAge() во время компиляции. Эти методы добавляются в байт-код, но не отображаются в исходном коде.

Нюансы:
Можно применять аннотации к отдельным полям, если не нужно генерировать методы для всех полей.

Уровень доступа методов можно изменить с помощью параметра AccessLevel:
@Getter(AccessLevel.PROTECTED)
private String name;


Аннотация @ToString

Эта аннотация автоматически генерирует метод toString(), который возвращает строковое представление объекта.

Пример:
import lombok.ToString;

@ToString
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует метод toString(), который выглядит примерно так:
@Override
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}


Нюансы:
Можно исключить поля из toString() с помощью параметра exclude:
@ToString(exclude = "age")
public class User {
private String name;
private int age;
}


Можно включить только определенные поля с помощью параметра of:
@ToString(of = "name")
public class User {
private String name;
private int age;
}


#Java #Training #Spring #Lombok #Getter #Setter #ToString
Lombok, базовые аннотации

Аннотация @EqualsAndHashCode

Эта аннотация автоматически генерирует методы equals() и hashCode(), которые используются для сравнения объектов.

Пример:
import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует методы equals() и hashCode(), которые сравнивают объекты по их полям. Например:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}


Нюансы:

Можно исключить поля из сравнения с помощью параметра exclude:

@EqualsAndHashCode(exclude = "age")
public class User {
private String name;
private int age;
}


Можно включить только определенные поля с помощью параметра of:
@EqualsAndHashCode(of = "name")
public class User {
private String name;
private int age;
}


Как
Lombok работает под капотом?

Lombok использует механизм обработки аннотаций (Annotation Processing) на этапе компиляции. Вот как это работает:

Компиляция Java-кода:
Когда вы компилируете код, компилятор Java (javac) запускает процессоры аннотаций.
Lombok подключается как процессор аннотаций.

Генерация кода:
Lombok анализирует аннотации в вашем коде.
На основе аннотаций Lombok генерирует дополнительные методы (например, геттеры, сеттеры, toString()).

Добавление в байт-код:
Сгенерированные методы добавляются в байт-код (.class файлы), но не отображаются в исходном коде.

Пример байт-кода:
Если вы используете @Getter, Lombok добавит методы getName() и getAge() в байт-код, но в исходном коде их не будет.

Нюансы использования Lombok

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

Совместимость с другими библиотеками:
Lombok может конфликтовать с некоторыми библиотеками, например, с Jackson или Hibernate. В таких случаях нужно быть осторожным с аннотациями.

Отладка:
Поскольку сгенерированный код не отображается в исходниках, отладка может быть сложнее. Убедитесь, что ваша IDE поддерживает Lombok.

Кастомизация:
Lombok предоставляет множество параметров для настройки аннотаций, таких как уровень доступа, исключение полей и т.д.

#Java #Training #Spring #Lombok #EqualsAndHashCode
Аннотации для конструкторов

Аннотация
@NoArgsConstructor

Эта аннотация генерирует конструктор без параметров (конструктор по умолчанию).

Пример использования:
import lombok.NoArgsConstructor;

@NoArgsConstructor
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует конструктор User() во время компиляции. Этот конструктор инициализирует объект без установки значений полей.

Нюансы:
Если в классе есть final-поля, то @NoArgsConstructor вызовет ошибку компиляции, так как final-поля должны быть инициализированы. В таком случае можно использовать параметр force, чтобы обнулить final-поля:

@NoArgsConstructor(force = true)
public class User {
private final String name = null; // final-поле будет инициализировано как null
private int age;
}


Аннотация @AllArgsConstructor

Эта аннотация генерирует конструктор со всеми параметрами для всех полей класса.

Пример использования:
import lombok.AllArgsConstructor;

@AllArgsConstructor
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует конструктор User(String name, int age), который принимает значения для всех полей класса.

Нюансы:
Если в классе есть static-поля, они не будут включены в конструктор, так как они принадлежат классу, а не объекту.

Можно использовать параметр staticName, чтобы создать статический фабричный метод вместо обычного конструктора:
@AllArgsConstructor(staticName = "of")
public class User {
private String name;
private int age;
}

// Использование:
User user = User.of("John", 30);


Аннотация @RequiredArgsConstructor

Эта аннотация генерирует конструктор для обязательных полей. К обязательным полям относятся:
Поля с модификатором final.
Поля, аннотированные
@NonNull.

Пример использования:
import lombok.RequiredArgsConstructor;
import lombok.NonNull;

@RequiredArgsConstructor
public class User {
@NonNull
private final String name;
private int age;
}


Как это работает:
Lombok генерирует конструктор User(String name), который принимает только обязательные поля (name в данном случае).

Нюансы:
Если поле @NonNull не инициализировано, Lombok добавит проверку на null в конструкторе:
public User(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
}
this.name = name;
}


Можно использовать параметр staticName, чтобы создать статический фабричный метод:


@RequiredArgsConstructor(staticName = "of")
public class User {
@NonNull
private final String name;
private int age;
}

// Использование:
User user = User.of("John");


#Java #Training #Spring #Lombok #AllArgsConstructor #NoArgsConstructor #RequiredArgsConstructor
Аннотации для управления состоянием

Комбинированная аннотация
@Data и нюансы использования

Аннотация @Data

Эта аннотация объединяет функциональность нескольких аннотаций:
@Getter и @Setter для всех полей.
@ToString для генерации метода toString().
@EqualsAndHashCode для генерации методов equals() и hashCode().
@RequiredArgsConstructor для генерации конструктора с обязательными полями.

Пример использования:
import lombok.Data;

@Data
public class User {
private String name;
private int age;
}


Как это работает:


Lombok генерирует следующие методы и конструктор:
Геттеры и сеттеры для всех полей.
Метод toString().
Методы equals() и hashCode().
Конструктор для обязательных полей (если такие есть).


Пример сгенерированного кода:
public class User {
private String name;
private int age;

public User() {}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}


Нюансы:
Если в классе есть final-поля или поля с @NonNull, Lombok добавит конструктор для этих полей.
Аннотация
@Data не генерирует конструктор по умолчанию (@NoArgsConstructor), если в классе есть final-поля.
Можно комбинировать
@Data с другими аннотациями, например, @Builder, чтобы добавить дополнительные возможности.

#Java #Training #Spring #Lombok #Data
Аннотации для работы с коллекциями

Аннотация @Builder

Эта аннотация реализует паттерн Builder, который позволяет создавать объекты через цепочку вызовов. Это особенно полезно для классов с большим количеством полей.

Пример использования:
import lombok.Builder;

@Builder
public class User {
private String name;
private int age;
}

// Использование:
User user = User.builder()
.name("John")
.age(30)
.build();


Как это работает:
Lombok генерирует внутренний класс UserBuilder с методами для каждого поля. В результате вы можете создавать объекты через цепочку вызовов, что делает код более читаемым.

Сгенерированный код:

public class User {
private String name;
private int age;

User(String name, int age) {
this.name = name;
this.age = age;
}

public static UserBuilder builder() {
return new UserBuilder();
}

public static class UserBuilder {
private String name;
private int age;

UserBuilder() {}

public UserBuilder name(String name) {
this.name = name;
return this;
}

public UserBuilder age(int age) {
this.age = age;
return this;
}

public User build() {
return new User(name, age);
}
}
}


Нюансы:
Можно использовать параметр builderMethodName, чтобы изменить имя метода builder():
@Builder(builderMethodName = "create")
public class User {
private String name;
private int age;
}

// Использование:
User user = User.create().name("John").age(30).build();
Если класс содержит final-поля, Lombok автоматически добавит их в билдер.


Аннотация @Singular

Эта аннотация используется вместе с @Builder для удобного добавления элементов в коллекции. Она генерирует методы для добавления элементов по одному.

Пример использования:
import lombok.Builder;
import lombok.Singular;
import java.util.List;

@Builder
public class User {
private String name;
@Singular
private List<String> hobbies;
}

// Использование:
User user = User.builder()
.name("John")
.hobby("Reading")
.hobby("Swimming")
.build();


Как это работает:
Lombok генерирует методы для добавления элементов в коллекцию. Например, для поля hobbies будет создан метод hobby(String hobby).

Сгенерированный код:
public class User {
private String name;
private List<String> hobbies;

User(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}

public static UserBuilder builder() {
return new UserBuilder();
}

public static class UserBuilder {
private String name;
private List<String> hobbies = new ArrayList<>();

UserBuilder() {}

public UserBuilder name(String name) {
this.name = name;
return this;
}

public UserBuilder hobby(String hobby) {
this.hobbies.add(hobby);
return this;
}

public User build() {
return new User(name, hobbies);
}
}
}


Нюансы:
Lombok автоматически определяет тип коллекции и генерирует соответствующие методы.
Можно использовать параметр value, чтобы изменить имя метода:
@Singular("interest")
private List<String> hobbies;

// Использование:
User user = User.builder().interest("Reading").interest("Swimming").build();


#Java #Training #Spring #Lombok #Singular #Builder
Аннотации для логирования

Аннотация @Slf4j

Эта аннотация автоматически создает логгер (SLF4J) для класса. Она избавляет от необходимости вручную создавать поле для логгера.

Пример использования:
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UserService {
public void createUser(String name) {
log.info("Creating user: {}", name);
}
}


Как это работает:
Lombok генерирует поле log типа Logger, которое можно использовать для логирования. Это поле инициализируется с помощью LoggerFactory.getLogger(ClassName.class).

Сгенерированный код:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);

public void createUser(String name) {
log.info("Creating user: {}", name);
}
}


Нюансы:
Аннотация @Slf4j поддерживает различные библиотеки логирования, такие как Log4j, Logback и другие, через SLF4J.
Если вы используете другую библиотеку логирования,
Lombok предоставляет аналогичные аннотации:
@Log4j для Log4j.
@Log4j2 для Log4j 2.
@Log для java.util.logging.


#Java #Training #Spring #Lombok #Slf4j
Аннотации для работы с исключениями

Аннотация @SneakyThrows — работа с исключениями

Аннотация @SneakyThrows позволяет "тихо" выбрасывать проверяемые исключения (checked exceptions) без необходимости объявлять их в сигнатуре метода или обрабатывать с помощью try-catch. Это может быть полезно в случаях, когда вы уверены, что исключение не произойдет, или когда вы хотите избежать излишнего boilerplate-кода.

Пример использования:
import lombok.SneakyThrows;

public class FileService {
@SneakyThrows
public void readFile(String path) {
// Код, который может выбросить IOException
Files.readAllBytes(Paths.get(path));
}
}


Как это работает под капотом?

Lombok использует хитрость с байт-кодом, чтобы обойти проверку компилятора на проверяемые исключения. Вот что происходит:

Генерация байт-кода:
Lombok оборачивает метод, помеченный @SneakyThrows, в блок try-catch.
Если возникает проверяемое исключение, оно "пробрасывается" как непроверяемое (unchecked exception).


Пример сгенерированного кода:
public void readFile(String path) {
try {
Files.readAllBytes(Paths.get(path));
} catch (IOException e) {
throw Lombok.sneakyThrow(e); // Преобразование в unchecked исключение
}
}


Метод Lombok.sneakyThrow:
Lombok добавляет статический метод sneakyThrow, который "обманывает" компилятор, заставляя его думать, что исключение непроверяемое:
public static <T extends Throwable> RuntimeException sneakyThrow(Throwable t) throws T {
throw (T) t;
}


Нюансы использования

Опасность скрытых исключений:
Использование @SneakyThrows может скрыть потенциальные ошибки, так как проверяемые исключения больше не видны в сигнатуре метода. Это может привести к трудноуловимым багам.

Несовместимость с некоторыми фреймворками:
Например, Spring AOP (аспектно-ориентированное программирование) может некорректно обрабатывать методы с @SneakyThrows, так как прокси-классы ожидают явного объявления исключений.

Ограничение на типы исключений:
По умолчанию @SneakyThrows обрабатывает все проверяемые исключения. Однако можно указать конкретные типы исключений:
@SneakyThrows(IOException.class)
public void readFile(String path) {
Files.readAllBytes(Paths.get(path));
}


Взаимодействие с другими аннотациями:
@SneakyThrows можно комбинировать с другими аннотациями Lombok, такими как @Getter, @Setter, @Data. Однако будьте осторожны при использовании с аннотациями, которые генерируют методы (например, @Builder), так как это может привести к неожиданному поведению.

#Java #Training #Spring #Lombok #SneakyThrows
Аннотации для работы с синхронизацией

Аннотация @Synchronized — управление синхронизацией

Аннотация @Synchronized позволяет создавать синхронизированные методы, аналогично использованию ключевого слова synchronized в Java. Однако @Synchronized предоставляет больше гибкости, так как позволяет синхронизировать методы на разных объектах.

Пример использования:
import lombok.Synchronized;

public class Counter {
private int count = 0;

@Synchronized
public void increment() {
count++;
}

@Synchronized("lock")
public void decrement() {
count--;
}

private final Object lock = new Object();
}


Как это работает под капотом?

Lombok генерирует код, который использует блоки synchronized для обеспечения потокобезопасности. Вот что происходит:

Генерация байт-кода:
Если аннотация @Synchronized используется без параметров, Lombok создает скрытое поле $lock и синхронизирует метод на этом поле.
Если указан параметр (например,
@Synchronized("lock")), Lombok синхронизирует метод на указанном объекте.

Пример сгенерированного кода:
public class Counter {
private int count = 0;
private final Object $lock = new Object(); // Скрытое поле для синхронизации
private final Object lock = new Object();

public void increment() {
synchronized ($lock) {
count++;
}
}

public void decrement() {
synchronized (lock) {
count--;
}
}
}


Создание скрытых полей:
Если объект для синхронизации не указан, Lombok создает скрытое поле $lock типа Object. Это поле используется для синхронизации всех методов, помеченных @Synchronized без параметров.

Нюансы использования

Производительность:
Использование @Synchronized может привести к снижению производительности, так как синхронизация блокирует выполнение кода в других потоках. Это особенно важно в высоконагруженных приложениях.

Взаимодействие с другими аннотациями:
@Synchronized можно комбинировать с другими аннотациями Lombok, такими как @Getter, @Setter, @Data. Однако будьте осторожны при использовании с аннотациями, которые генерируют методы (например, @Builder), так как это может привести к неожиданному поведению.

Ограничение на объекты синхронизации:
Объект, указанный в @Synchronized("lock"), должен быть финальным (final), чтобы избежать изменений во время выполнения. В противном случае синхронизация может работать некорректно.

Проблемы с наследованием:
Если метод, помеченный @Synchronized, переопределяется в подклассе, синхронизация может не работать, так как Lombok генерирует код только для текущего класса.

Альтернативы
Вместо
@Synchronized можно использовать более современные механизмы синхронизации, такие как ReentrantLock или StampedLock, которые предоставляют больше гибкости и контроля.

#Java #Training #Spring #Lombok #Synchronized
Аннотации для работы с данными — @Value и @With

Аннотация
@Value

Аннотация @Value используется для создания неизменяемых (immutable) объектов. Она объединяет функциональность нескольких аннотаций:
@Getter — генерирует геттеры для всех полей.
@AllArgsConstructor — генерирует конструктор со всеми параметрами.
@ToString — генерирует метод toString().
@EqualsAndHashCode — генерирует методы equals() и hashCode().
Все поля класса становятся final.

Пример использования:
import lombok.Value;

@Value
public class User {
String name;
int age;
}


Как это работает под капотом:
Генерация кода:
Lombok создает класс с final-полями, геттерами, конструктором, toString(), equals() и hashCode().

Пример сгенерированного кода:
public final class User {
private final String name;
private final int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

@Override
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}


Нюансы использования:

Класс становится неизменяемым: все поля final, сеттеры не генерируются.
Если нужно изменить значение поля, придется создавать новый объект.
Нельзя использовать
@Value с @Setter или @Data, так как они конфликтуют.

Взаимодействие с другими аннотациями:
@Value можно комбинировать с @Builder, чтобы добавить поддержку паттерна Builder для создания объектов.
Нельзя использовать с
@NoArgsConstructor, так как все поля final и должны быть инициализированы.

Аннотация @With

Аннотация @With генерирует методы для создания копии объекта с измененным значением одного поля. Это полезно для работы с неизменяемыми объектами.

Пример использования:
import lombok.With;

@With
public class User {
private final String name;
private final int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}
}

// Использование:
User user = new User("John", 30);
User newUser = user.withAge(31); // Создает новый объект с измененным возрастом


Как это работает под капотом

Генерация кода:
Lombok создает методы withFieldName() для каждого поля.

Пример сгенерированного кода:
public class User {
private final String name;
private final int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public User withName(String name) {
return this.name == name ? this : new User(name, this.age);
}

public User withAge(int age) {
return this.age == age ? this : new User(this.name, age);
}
}


Нюансы использования

@With работает только с final-полями, так как предназначен для неизменяемых объектов.
Если поле не final,
Lombok выдаст предупреждение.
Можно использовать с
@Value для создания полностью неизменяемых объектов.

Взаимодействие с другими аннотациями:
@With можно комбинировать с @Value, @Builder и другими аннотациями Lombok.
Нельзя использовать с
@Data, так как @Data генерирует сеттеры, что противоречит идее неизменяемости.

#Java #Training #Spring #Lombok #With #Value
Создание кастомных аннотаций с использованием Lombok

Lombok позволяет создавать кастомные аннотации, которые объединяют несколько аннотаций Lombok в одну. Это полезно, если вы часто используете одни и те же комбинации аннотаций.

Пример создания кастомной аннотации

Шаг 1: Создайте аннотацию:
import lombok.Value;
import lombok.With;
import lombok.experimental.Accessors;

@Value
@With
@Accessors(fluent = true)
public @interface ImmutableUser {
}


Шаг 2: Используйте аннотацию:
@ImmutableUser
public class User {
String name;
int age;
}


Результат:
Класс User будет иметь все функциональности
@Value, @With и @Accessors(fluent = true).

Как это работает под капотом

Компиляция Java-кода:
Когда вы компилируете код, Lombok анализирует кастомную аннотацию и применяет все аннотации, которые она объединяет.

Генерация кода:
Lombok генерирует код для каждой аннотации, указанной в кастомной аннотации.
Например, для @ImmutableUser будет сгенерирован код для @Value, @With и @Accessors(fluent = true).

Нюансы использования кастомных аннотаций

Ограничения:
Кастомные аннотации могут объединять только аннотации Lombok.
Нельзя использовать кастомные аннотации для добавления новой функциональности, только для комбинирования существующей.

Взаимодействие с другими аннотациями:
Кастомные аннотации можно комбинировать с другими аннотациями Lombok.
Например, можно добавить
@Builder к кастомной аннотации:

@Value
@With
@Builder
public @interface ImmutableUserWithBuilder {
}


Проблемы:

Если кастомная аннотация объединяет конфликтующие аннотации (например, @Value и @Data), это вызовет ошибку компиляции.
Убедитесь, что все аннотации в кастомной аннотации совместимы друг с другом.


#Java #Training #Spring #Lombok #CustomAnnotation