Введение

Как разработчики Java, мы часто увязаем в шаблонном коде. Методы доступа, методы-мутаторы, конструкторы equals(), hashCode() и toString() необходимы, но занимают много места и отвлекают от основной логики приложения. Spring Framework, широко используемый для создания корпоративных приложений, является сторонником сокращения шаблонов. Однако даже при использовании Spring определенное количество этого неизбежно — если только мы не добавим в уравнение Project Lombok.

Project Lombok предоставляет аннотации, которые могут значительно минимизировать шаблонный код, улучшая читаемость и удобство обслуживания. Среди этих аннотаций обычно используются @FieldDefaults и @Data, и в этом посте мы подробно рассмотрим эти две, продемонстрировав их полезность в приложении Spring.

Знакомство с Ломбоком

Project Lombok — это библиотека, которая существенно повлияла на экосистему Java, сократив количество повторяющегося и рутинного кода, который часто приходится писать разработчикам. Хотя Java — мощный и универсальный язык, его часто критикуют за многословие, особенно по сравнению с более современными языками, такими как Kotlin или Python. Разработчикам часто приходится писать много «церемониального» кода только для того, чтобы заставить работать простые вещи, такие как создание простых старых Java-объектов (POJO) с соответствующими методами получения, установки, equals(), hashCode() и toString().

Что хочет решить Ломбок

Многословие Java — это не просто вопрос эстетики; это может напрямую повлиять на продуктивность команды разработчиков. Написание шаблонного кода отнимает много времени и увеличивает вероятность ошибок. Представьте себе класс с множеством полей, каждое из которых требует своего собственного метода получения и установки. Ситуация быстро превращается в кошмар обслуживания, особенно когда вы начинаете добавлять такие методы, как equals() и hashCode(), которые должны быть согласованы друг с другом и обновляться каждый раз при изменении полей класса.

Здесь в игру вступает Ломбок. Предоставляя набор аннотаций, Lombok автоматически генерирует код во время компиляции, избегая многословия и вероятности человеческой ошибки. Конечным результатом является более читаемая и поддерживаемая кодовая база, которую легче понимать, отлаживать и расширять.

Как работает Ломбок

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

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

Настройка Lombok в вашем проекте

Интеграция Lombok в проект Spring — простой процесс. Если вы используете Maven, вы можете добавить в свой pom.xml следующую зависимость:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version> <!-- Use the latest version -->
</dependency>

Или, если вы используете Gradle, включите это в свой build.gradle:

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.22' // Use the latest version
    annotationProcessor 'org.projectlombok:lombok:1.18.22'
}

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

Популярные аннотации Ломбока

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

  • @Getter и @Setter: автоматически создавать методы получения и установки для полей.
  • @ToString:Создает удобочитаемый метод toString().
  • @EqualsAndHashCode: Генерирует методы equals() и hashCode() на основе полей вашего класса.
  • @AllArgsConstructor, @NoArgsConstructor, @RequiredArgsConstructor: Создание конструкторов с различными конфигурациями.

Однако в этом посте основное внимание будет уделено аннотациям @FieldDefaults и @Data, которые очень полезны в контексте приложений Spring.

@Data Аннотация

Аннотация @Data Project Lombok — это, по сути, швейцарский нож для ваших занятий по Java. С помощью одной аннотации вы можете автоматически генерировать различные методы, которые в противном случае вам пришлось бы создавать вручную — методы, которые часто просты, но могут быстро загромождать вашу кодовую базу. К ним относятся методы получения и установки, методы equals(), hashCode() и toString(). Хотя некоторые могут возразить, что подход «один размер подходит всем» не идеален для каждой ситуации, нельзя отрицать, что @Data чрезвычайно полезен во многих сценариях, особенно для POJO — простых старых объектов Java, которые действуют как носители данных в приложении.

Анатомия @Data

Когда вы аннотируете класс с помощью @Data, Lombok генерирует:

  • Методы получения для всех нестатических полей
  • Методы установки для всех нефинальных, нестатических полей
  • Метод equals(), проверяющий равенство полей
  • Метод hashCode(), вычисляющий хэш-код на основе полей
  • Метод toString(), возвращающий строковое представление объекта.

Эта аннотация мета-аннотирована другими аннотациями Lombok, такими как @Getter, @Setter, @ToString и @EqualsAndHashCode. Следовательно, использование @Data эквивалентно одновременному использованию всех этих аннотаций.

Вот простой пример, иллюстрирующий, что делает @Data.

Без Ломбока:

public class Book {
    private String title;
    private String author;
    private int pages;

    public Book() {
    }

    public Book(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    @Override
    public boolean equals(Object o) {
        // Implementation
    }

    @Override
    public int hashCode() {
        // Implementation
    }

    @Override
    public String toString() {
        // Implementation
    }
}

С @Data Ломбока:

import lombok.Data;

@Data
public class Book {
    private String title;
    private String author;
    private int pages;
}

Как видите, последний пример намного чище и целенаправленнее, позволяя вам сосредоточиться на реальной бизнес-логике вашего приложения, а не теряться в шаблонном коде.

Компромиссы и соображения

Хотя @Data является мощным инструментом, важно понимать его ограничения и учитывать, когда его использовать, а когда избегать.

  1. Производительность.Автоматически созданные методы equals() и hashCode() могут быть неэффективны для больших классов или сложных иерархий. В таких случаях вам может быть лучше использовать собственные реализации.
  2. Неизменяемость:@Data генерирует сеттеры для всех нефинальных полей, что делает класс изменяемым. Если неизменность является требованием, возможно, вы не захотите использовать эту аннотацию или рассмотрите возможность ее использования в сочетании с @FieldDefaults, чтобы сделать поля окончательными.
  3. Накладные расходы.При использовании @Data легко потерять представление о том, что происходит под капотом. Разработчикам, незнакомым с Lombok, может быть сложно работать с классами, где значительная функциональность скрыта за одной аннотацией.

Настройка поведения

Lombok предоставляет способы настройки поведения @Data. Например, вы можете исключить определенные поля из сгенерированных методов equals() и hashCode(), пометив их @EqualsAndHashCode.Exclude. Аналогичным образом вы можете настроить свое представление toString(), используя @ToString.Exclude для полей, которые вы не хотите включать.

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
public class CustomBook {
    private String title;
    private String author;
    
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private int pages;
}

В этом измененном примере поле pages исключено из методов equals() и hashCode(), а также из метода toString().

Подводя итог, можно сказать, что аннотация @Data — невероятно универсальный инструмент для любого инструментария разработчика Java, особенно для тех, кто использует Spring Framework. Устранив шаблонный код, вы можете писать более краткий и читаемый код, хотя он имеет свой собственный набор компромиссов, которые необходимо тщательно учитывать.

@FieldDefaults Аннотация

Одна из менее обсуждаемых, но не менее мощных аннотаций Lombok — @FieldDefaults. Эта аннотация позволяет установить модификаторы по умолчанию для полей в классе. Вы можете контролировать уровень доступа к полям и указывать, должны ли они быть final. Это особенно полезно в приложениях на основе Spring, где вам может потребоваться установить согласованный контроль доступа на уровне полей без явного объявления его для каждого поля.

Что делает @FieldDefaults

Когда вы аннотируете класс с помощью @FieldDefaults, вы можете указать два ключевых элемента:

  1. Уровень доступа. Видимость полей, например PRIVATE, PROTECTED, PACKAGE или PUBLIC. Значение по умолчанию — PRIVATE.
  2. Final: логическое значение, определяющее, должны ли поля иметь значение final. Значение по умолчанию — false.

Используя эту аннотацию, вы можете удобно установить поведение по умолчанию для всех полей в классе, тем самым обеспечивая более чистый и согласованный код. Аннотация может быть особенно эффективной в сочетании с другими аннотациями Lombok, такими как @Data.

Основное использование

Вот простой пример, показывающий, как использовать @FieldDefaults:

import lombok.experimental.FieldDefaults;
import lombok.AccessLevel;

@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class ImmutablePerson {
    String name;
    int age;
}

В этом примере мы установили для всех полей значения private и final. Без @FieldDefaults вам пришлось бы явно объявить каждое поле как private final.

Объединение с другими аннотациями

Вы часто можете видеть, как @FieldDefaults используется вместе с @Data в приложениях Spring. В то время как @Data предоставляет множество функций, таких как методы получения, установки и методы equals() и hashCode(), @FieldDefaults гарантирует, что все поля являются частными и потенциально неизменяемыми. Это может создать надежный класс модели, который будет лаконичным и безопасным.

import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.AccessLevel;

@Data
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class SecureBook {
    String title;
    String author;
    int pages;
}

В этом модифицированном примере класса Book мы не только обладаем всеми функциями, предоставляемыми @Data, но также обеспечиваем, чтобы все поля были private и final, тем самым улучшая неизменяемость и инкапсуляцию класса.

Примечание об атрибуте makeFinal

Настройка makeFinal = true хорошо работает при создании неизменяемых классов, но она ограничивает функциональность других аннотаций Lombok, таких как @Setter. В классах, помеченных @Data, параметр makeFinal = true по существу отключит генерацию методов установки, поскольку поля final не могут быть изменены после инициализации.

Поэтому при использовании @FieldDefaults в сочетании с @Data необходимо учитывать, является ли неизменяемость более важной, чем возможность изменять значения полей.

Аннотация @FieldDefaults предоставляет элегантный способ установить настройки уровня поля по умолчанию для класса Java. Хотя аннотация может показаться простой, ее влияние на читаемость и удобство сопровождения кода может быть значительным. При разумном использовании, особенно в сочетании с другими аннотациями Lombok, такими как @Data, @FieldDefaults, вы можете создать более чистый и эффективный код с меньшим количеством строк и большей согласованностью.

Когда использовать, а когда не использовать

Решение о том, когда использовать аннотации Lombok @Data и @FieldDefaults, не является однозначным решением. Хотя эти аннотации предлагают множество преимуществ, таких как более чистый и удобный в обслуживании код, у них есть свои особенности. Ниже мы разберем некоторые сценарии, в которых использование этих аннотаций может оказаться полезным, а также некоторые случаи, когда следует действовать с осторожностью.

Когда использовать @Data и @FieldDefaults

  1. Быстрое прототипирование. Если вам нужно быстро разработать прототипы или проверить концепцию, эти аннотации могут значительно ускорить процесс написания кода.
  2. POJO/классы данных. Если у вас есть простые классы, предназначенные в первую очередь для хранения данных, и вы уверены, что вам потребуются все методы, которые генерирует @Data, использование этой аннотации не составит труда.
  3. Приложения Spring Boot: Для приложений Spring Boot, которые часто ценят соглашение, а не конфигурацию, использование @Data и @FieldDefaults хорошо согласуется с философией платформы по сокращению шаблонного кода.
  4. Недолговечные проекты. В проектах с коротким сроком существования, где удобство сопровождения не является серьезной проблемой, использование этих аннотаций может сделать процесс разработки более эффективным.
  5. Последовательность команды. Если все члены команды знакомы с Lombok и его аннотациями, использование @Data и @FieldDefaults может сделать код более единообразным и простым для понимания.

Когда не использовать @Data и @FieldDefaults

  1. Сложная бизнес-логика. Для классов со сложной бизнес-логикой использование @Data может быть опасным, поскольку оно генерирует методы установки, которые могут обойти вашу логику. В этих сценариях написание методов вручную может дать вам больший контроль.
  2. Неизменяемость. Если вашему приложению требуются неизменяемые объекты, будьте осторожны с @Data, поскольку он по умолчанию генерирует установщики. Вы по-прежнему можете использовать @FieldDefaults(makeFinal = true), чтобы сделать поля окончательными, но это сделает спорной функцию создания установщика аннотации @Data.
  3. Большая команда с разным уровнем навыков. В команде, где не все знакомы с Lombok, использование этих аннотаций может усложнить обучение и затруднить понимание кодовой базы новичками.
  4. Разработка библиотеки. Если вы разрабатываете библиотеку, предназначенную для публичного или широкого внутреннего использования, использование аннотаций Lombok может вызвать зависимости или неожиданное поведение ваших пользователей.
  5. Код, критичный к производительности.В сценариях, где производительность имеет решающее значение, автоматически созданные методы могут быть не так оптимизированы, как написанные вручную, особенно для таких операций, как equals() и hashCode() с большими объектами.
  6. Наследование и полиморфизм. @Data генерирует методы equals() и hashCode(), которые могут не соответствовать контракту, необходимому при расширении класса или реализации интерфейса, который имеет свои собственные реализации equals() и hashCode(). В таких случаях рекомендуется писать эти методы вручную.

Хотя @Data и @FieldDefaults могут значительно сократить шаблонный код и сделать ваш код чище, их использование следует тщательно продумать. Они являются отличными инструментами для упрощения разработки, но не являются панацеей для всех сценариев. Знание того, когда использовать эти аннотации — и, что, возможно, более важно, когда их не использовать — может иметь решающее значение для поддержания сбалансированной, эффективной и понятной базы кода.

Заключение

Аннотации @FieldDefaults и @Data Project Lombok могут значительно сократить количество шаблонного кода в ваших приложениях Spring, делая код более читабельным и удобным в сопровождении. Однако следует быть осторожным с тем, когда и где использовать эти аннотации. Они чрезвычайно полезны для простых классов данных, но могут не подходить для классов, которые содержат сложное поведение или требуют специальной реализации таких методов, как equals(), hashCode() или toString().

Добившись баланса, вы сможете максимально эффективно использовать эти мощные аннотации и писать более чистый и удобный в сопровождении код.

  1. Официальная документация Ломбок
  2. Официальная документация Java