Рубрики
Без рубрики

Использование аннотации @Singular со строителями Ломбока

Узнайте, как использовать аннотацию @Singular для создания удобных построителей с помощью Lombok

Автор оригинала: baeldung.

1. Обзор

Библиотека Ломбока предоставляет отличный способ упрощения объектов данных. Одной из ключевых особенностей Project Lombok является аннотация @Builder , которая автоматически создает классы Builder для создания неизменяемых объектов. Однако заполнение коллекций в наших объектах может быть неуклюжим со стандартными классами Builder , созданными на Ломбоке.

В этом уроке мы рассмотрим аннотацию @Singular , которая помогает нам работать с коллекциями в наших объектах данных. Как мы увидим, он также обеспечивает соблюдение передовой практики.

2. Строители и коллекции

Классы Builder позволяют легко создавать неизменяемые объекты данных с помощью их простого и плавного синтаксиса. Давайте рассмотрим пример классов, аннотированных аннотацией Ломбока @Builder :

@Getter
@Builder
public class Person {
    private final String givenName;
    private final String additionalName;
    private final String familyName;
    private final List tags;
}

Теперь мы можем создавать экземпляры Person с помощью шаблона builder. Обратите внимание, что свойство tags является List . Кроме того, стандартный Lombok @Builder предоставит методы для установки этого свойства, как и для свойств, не входящих в список:

Person person = Person.builder()
  .givenName("Aaron")
  .additionalName("A")
  .familyName("Aardvark")
  .tags(Arrays.asList("fictional","incidental"))
  .build();

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

2.1. Использование аннотации @Singular со списками

Давайте добавим еще один Список к нашему Человеку объекту и аннотируем его @Singular . Это даст нам бок о бок представление одного поля, которое аннотировано, и одного, которое не аннотировано. Помимо общих тегов свойств, мы добавим список интересов к нашему Лицу :

@Singular private final List interests;

Теперь мы можем составить список значений по одному:

Person person = Person.builder()
  .givenName("Aaron")
  .additionalName("A")
  .familyName("Aardvark")
  .interest("history")
  .interest("sport")
  .build();

Конструктор сохранит каждый элемент внутри в List и создаст соответствующую Коллекцию , когда мы вызовем build() .

2.2. Работа с другими типами коллекций

Мы проиллюстрировали @Singular работу с java.util.Список здесь, но он также может быть применен к другим классам Java Collection /. Давайте добавим еще несколько членов в наш Person :

@Singular private final Set skills;
@Singular private final Map awards;

A Set будет вести себя так же , как List , что касается Builder s – мы можем добавлять элементы один за другим:

Person person = Person.builder()
  .givenName("Aaron")
  .skill("singing")
  .skill("dancing")
  .build();

Поскольку Set не поддерживает дубликаты, мы должны знать, что добавление одного и того же элемента несколько раз не приведет к созданию нескольких элементов. Строитель отнесется к этой ситуации снисходительно. Мы можем добавить элемент несколько раз, но созданный Set будет иметь только одно вхождение элемента.

Map s обрабатываются несколько иначе, с помощью методов Builder exposing, которые принимают ключ и значение соответствующих типов:

Person person = Person.builder()
  .givenName("Aaron")
  .award("Singer of the Year", LocalDate.now().minusYears(5))
  .award("Best Dancer", LocalDate.now().minusYears(2))
  .build();

Как мы видели с Set s, конструктор снисходителен к дубликатам ключей Map и будет использовать последнее значение, если один и тот же ключ назначается более одного раза.

3. Именование @Сингулярных методов

До сих пор мы полагались на один кусочек магии в аннотации @Singular , не привлекая к ней внимания. Сам Builder предоставляет метод для назначения всей коллекции сразу, который использует форму множественного числа – ” награды “, например. Дополнительные методы, добавленные аннотацией @Singular , используют единственную форму – например, ” award “.

Ломбок достаточно умен, чтобы распознавать простые слова множественного числа в английском языке, где они следуют регулярному шаблону. Во всех примерах, которые мы использовали до сих пор, он просто удаляет последнюю букву “s”.

Он также будет знать, что для некоторых слов, оканчивающихся на “es”, необходимо удалить последние две буквы. Он знает, например, что “трава” – это единственное число “трав”, и что “виноград”, а не “виноград”, является единственным числом “винограда”. В некоторых случаях, однако, мы должны оказать ему некоторую помощь.

Давайте построим простую модель моря, содержащего рыбу и морские травы:

@Getter
@Builder
public class Sea {
    @Singular private final List grasses;
    @Singular private final List fish;
}

Ломбок может справиться со словом “трава”, но теряется с “рыбой”. В английском языке формы единственного и множественного числа, как ни странно, одинаковы. Этот код не будет компилироваться, и мы получим ошибку:

Can't singularize this name; please specify the singular explicitly (i.e. @Singular("sheep"))

Мы можем разобраться в этом, добавив значение в аннотацию для использования в качестве имени единственного метода:

@Singular("oneFish") private final List fish;

Теперь мы можем скомпилировать наш код и использовать Builder :

Sea sea = Sea.builder()
  .grass("Dulse")
  .grass("Kelp")
  .oneFish("Cod")
  .oneFish("Mackerel")
  .build();

В данном случае мы выбрали довольно надуманный one Fish () , но тот же метод можно использовать и с нестандартными словами , которые имеют отчетливое множественное число. Например, Список | детей может быть предоставлен методом child() .

4. Неизменность

Мы видели, как аннотация @Singular помогает нам работать с коллекциями на Ломбоке. Помимо удобства и выразительности, он также может помочь нам сохранить наш код в чистоте.

Неизменяемые объекты определяются как объекты, которые не могут быть изменены после их создания. Например, неизменяемость важна в реактивных архитектурах, поскольку она позволяет нам передавать объект в метод с гарантией отсутствия побочных эффектов. Шаблон Builder чаще всего используется в качестве альтернативы геттерам и сеттерам POJO для поддержки неизменяемости.

Когда наши объекты данных содержат классы Collection , может быть легко позволить неизменности немного ускользнуть. Интерфейсы базовой коллекции — List , Set и Map — все имеют изменяемые и неизменяемые реализации. Если мы полагаемся на стандартный конструктор Ломбоков, мы можем случайно передать изменяемую коллекцию, а затем изменить ее:

List tags= new ArrayList();
tags.add("fictional");
tags.add("incidental");
Person person = Person.builder()
  .givenName("Aaron")
  .tags(tags)
  .build();
person.getTags().clear();
person.getTags().add("non-fictional");
person.getTags().add("important");

Нам пришлось довольно много работать в этом простом примере, чтобы совершить ошибку. Если бы мы использовали Arrays.asList() , например, для построения переменной tags , мы бы получили неизменяемый список бесплатно, а вызовы add() или clear() вызвали бы исключение UnsupportedOperationException .

В реальном кодировании ошибка с большей вероятностью возникнет, если, например, коллекция передается в качестве параметра. Тем не менее, полезно знать, что с помощью @Singular мы можем работать с базовыми интерфейсами Collection и получать неизменяемые экземпляры при вызове build() .

5. Заключение

В этом уроке мы видели, как аннотация Lombok @Singular обеспечивает удобный способ работы с интерфейсами List , Set и Map с использованием шаблона Builder. Шаблон Builder поддерживает неизменяемость, и @Singular предоставляет нам первоклассную поддержку для этого.

Как обычно, полные примеры кода доступны на GitHub .