Создание кастомных аннотаций с использованием Lombok
Lombok позволяет создавать кастомные аннотации, которые объединяют несколько аннотаций Lombok в одну. Это полезно, если вы часто используете одни и те же комбинации аннотаций.
@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), это вызовет ошибку компиляции. Убедитесь, что все аннотации в кастомной аннотации совместимы друг с другом.
Аннотация @Cleanup автоматически закрывает ресурсы, такие как потоки, сокеты или соединения с базой данных, после их использования. Это избавляет от необходимости вручную вызывать метод close().
public class FileService { public void readFile(String path) throws IOException { @Cleanup InputStream inputStream = new FileInputStream(path); // Использование inputStream } }
Как это работает под капотом: Lombok оборачивает использование ресурса в блок try-finally и вызывает метод close() в блоке finally.
Сгенерированный код:
public void readFile(String path) throws IOException { InputStream inputStream = new FileInputStream(path); try { // Использование inputStream } finally { if (inputStream != null) { inputStream.close(); } } }
Нюансы использования: Ресурс должен реализовывать интерфейс java.io.Closeable или java.lang.AutoCloseable. Если метод close() выбрасывает исключение, оно будет подавлено (если не указано иное).
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class User { String name; int age; }
Как это работает под капотом: Lombok добавляет указанные модификаторы доступа ко всем полям класса.
Сгенерированный код:
public class User { private final String name; private final int age; }
Нюансы использования: Можно указать уровень доступа (PRIVATE, PUBLIC, PROTECTED) и добавить final к полям. Если поле уже имеет модификатор доступа, он не будет изменен.
public class Task170125_1 { public static void main(String[] args) { int i = 0; while (i++ < 5) { if (i == 3) continue; System.out.print(i + " "); } } }
@ExtensionMethod(Collections.class) public class ListUtils { public void example() { java.util.List<String> list = new java.util.ArrayList<>(); list.add("Hello"); list = unmodifiableList(list); // Метод из Collections } }
Как это работает под капотом: Lombok преобразует вызовы методов-расширений в вызовы статических методов.
Сгенерированный код:
public class ListUtils { public void example() { java.util.List<String> list = new java.util.ArrayList<>(); list.add("Hello"); list = Collections.unmodifiableList(list); } }
Нюансы использования: Методы-расширения должны быть статическими. Аннотация находится в экспериментальном статусе и может измениться в будущем.
Аннотация @Wither создает методы для создания копии объекта с измененным значением одного поля. Это аналог @With, но находится в экспериментальном статусе.
Пример использования:
import lombok.experimental.Wither;
@Wither 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); } }
Нюансы использования: Работает только с final-полями. Находится в экспериментальном статусе.
Аннотация @Helper создает локальные вспомогательные классы внутри методов.
Пример использования:
import lombok.experimental.Helper;
public class Example { public void exampleMethod() { @Helper class LocalHelper { void help() { System.out.println("Helping!"); } } new LocalHelper().help(); } }
Как это работает под капотом: Lombok создает локальный класс, который можно использовать внутри метода.
Сгенерированный код:
public class Example { public void exampleMethod() { class LocalHelper { void help() { System.out.println("Helping!"); } } new LocalHelper().help(); } }
Нюансы использования: Находится в экспериментальном статусе. Полезно для организации кода внутри методов.
Лучшие практики использования Lombok Когда использовать Lombok: Для сокращения шаблонного кода (геттеры, сеттеры, конструкторы). Для улучшения читаемости кода.
Как избежать злоупотребления: Не используйте Lombok для сложной логики. Избегайте чрезмерного использования аннотаций, таких как @Data или @AllArgsConstructor.
Совместимость с другими библиотеками:
Lombok может конфликтовать с Jackson, Hibernate и другими библиотеками. Будьте осторожны с аннотациями, которые генерируют методы.
Проблемы и ограничения Lombok
Проблемы с поддержкой в IDE: Некоторые IDE могут некорректно обрабатывать код с Lombok. Убедитесь, что плагин Lombok установлен и настроен.
Ограничения при использовании с другими инструментами: Например, Jackson может не работать с @Data, если не настроены правильные геттеры и сеттеры.
Отладка: Поскольку сгенерированный код не отображается в исходниках, отладка может быть сложнее. Убедитесь, что ваша IDE поддерживает Lombok.
А я на завтра ничего не подготовил... Может кто хочет что-нибудь показать?🧐
Кроме этого, у нас знаменательное событие - канал устойчиво достиг количества в 5⃣0⃣0️⃣ подписчиков 🥳
Много это или мало? Лично для меня - огромно! Вспоминая как начинался этот канал с 15 подписчиками из интенсива JavaRush, сейчас цифра 500 человек воспринимается как значительное достижение. Хотелось бы сказать "Спасибо 🙂" каждому, кто осознанно приписывает себя в постоянный состав подписчиков, тех кто не стесняясь приходит на воскресные встречи и принимает непосредственное участие в жизни канала. Я рад что вы со мной.🤝
А канал однозначно будет развиваться дальше. После изучения Java Core и Spring, пройдемся по фраймворкам с которыми взаимодействуешь каждый день, потом более редким, но эффективным и так далее... Будем однозначно встречаться по воскресениям и писать что-то злободневное для новичков 😁
Уверен что совместно, мы сделаем наш канал отличным подспорьем для развития Javистов-новичков, а так же платформой где можно пообщаться и узнать что-то новое!👍
И не стесняйтесь что-то предлагать для развития канала!
Я не SMM-щик и в силу того, что все это делаю один, мне элементарно порой не хватает времени на свежие идеи 😎
Эта фраза принадлежит легендарному физику и нобелевскому лауреату Ричарду Фейнману. Ее суть — в идее, что настоящее понимание приходит только через действие. Если ты не можешь что-то воссоздать, объяснить или построить с нуля, значит, твое знание поверхностно.
Фейнман использовал этот принцип в обучении и науке, показывая, что глубина понимания важнее заучивания фактов. Отличный повод задуматься: насколько хорошо мы действительно разбираемся в том, что считаем знакомым?
Начинаем серию постов для углубления недостаточно рассмотренных и актуальных тем.
Классы для работы с датами и временем в Java Time API
Java Time API (пакет java.time) был введен в Java 8 для замены устаревших классов Date и Calendar. Он предоставляет удобные и неизменяемые (immutable) классы для работы с датами и временем.
1. LocalDate
Класс LocalDate представляет дату без времени и временной зоны. Он используется для работы с датами, такими как дни рождения, сроки выполнения задач и т.д.
Пример использования:
LocalDate today = LocalDate.now(); // Текущая дата LocalDate specificDate = LocalDate.of(2023, 10, 15); // Конкретная дата
Плюсы: Простота использования. Неизменяемость (immutable), что делает его потокобезопасным.
Минусы: Не поддерживает время и временные зоны.
2. LocalTime
Класс LocalTime представляет время без даты и временной зоны. Он используется для работы с временем, например, для учета времени начала и окончания событий. Пример использования:
LocalTime now = LocalTime.now(); // Текущее время LocalTime specificTime = LocalTime.of(14, 30); // Конкретное время
Плюсы: Удобен для работы только с временем. Неизменяемость.
Минусы: Не поддерживает даты и временные зоны.
3. LocalDateTime
Класс LocalDateTime объединяет LocalDate и LocalTime, представляя дату и время без временной зоны. Он используется, когда нужно работать с датой и временем одновременно.
Пример использования:
LocalDateTime now = LocalDateTime.now(); // Текущая дата и время LocalDateTime specificDateTime = LocalDateTime.of(2023, 10, 15, 14, 30); // Конкретная дата и время
System.out.println("Сейчас: " + now); System.out.println("Конкретная дата и время: " + specificDateTime);
Плюсы: Удобен для работы с датой и временем. Неизменяемость.
Минусы: Не поддерживает временные зоны.
4. ZonedDateTime
Класс ZonedDateTime расширяет LocalDateTime, добавляя информацию о временной зоне. Он используется для работы с датой и временем в конкретной временной зоне.
Пример использования:
ZonedDateTime nowInTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo")); // Текущее время в Токио ZonedDateTime specificDateTimeInNewYork = ZonedDateTime.of(2023, 10, 15, 14, 30, 0, 0, ZoneId.of("America/New_York"));
System.out.println("Сейчас в Токио: " + nowInTokyo); System.out.println("Конкретное время в Нью-Йорке: " + specificDateTimeInNewYork);
Плюсы: Поддержка временных зон. Неизменяемость.
Минусы: Сложнее в использовании из-за необходимости учитывать временные зоны.
5. Форматирование и парсинг: DateTimeFormatter
Класс DateTimeFormatter используется для форматирования и парсинга дат и времени. Он поддерживает множество предопределенных форматов, а также позволяет создавать собственные.
Пример использования:
LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
String formattedDateTime = now.format(formatter); // Форматирование System.out.println("Форматированная дата и время: " + formattedDateTime);
LocalDateTime parsedDateTime = LocalDateTime.parse("15.10.2023 14:30:00", formatter); // Парсинг System.out.println("Распарсенная дата и время: " + parsedDateTime);
Плюсы: Гибкость в настройке форматов. Поддержка локализации.
Минусы: Требует внимательности при указании форматов, чтобы избежать ошибок.