1. Обзор
Как мы знаем, метод toString()
используется для получения строкового представления объекта Java.
Project Lombok может помочь нам генерировать согласованные строковые представления без шаблонов и загромождения исходного кода. Это также может повысить удобство сопровождения, особенно в тех случаях, когда классы могут содержать большое количество полей.
В этом руководстве мы увидим, как автоматически сгенерировать этот метод и различные параметры конфигурации, доступные для дальнейшей тонкой настройки результирующего вывода.
2. Настройка
Начнем с включения зависимости Project Lombok в наш пример проекта:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
В наших примерах мы будем использовать простой класс Account
POJO с несколькими полями, чтобы продемонстрировать функциональность и различные параметры конфигурации.
3. Основное использование
Мы можем аннотировать любой класс аннотацией Lombok @ToString
. Это изменяет сгенерированный байт-код и создает реализацию метода toString()
.
Давайте применим эту аннотацию к нашей простой учетной записи
POJO:
@ToString
public class Account {
private String id;
private String name;
// standard getters and setters
}
По умолчанию аннотация @ToString
печатает имя класса вместе с именем каждого нестатического поля и его значением, полученным путем вызова метода получения (если он объявлен) `` . Поля также отображаются в соответствии с порядком объявления в исходном классе . Запятая разделяет разные пары значений поля.
Теперь вызов метода toString()
для экземпляра этого класса генерирует следующий вывод:
Account(id=12345, name=An account)
В большинстве случаев этого достаточно для создания стандартного и удобного строкового представления объектов Java.
4. Параметры конфигурации
Доступно несколько параметров конфигурации, которые позволяют нам изменять и настраивать сгенерированный метод toString()
. Они могут быть полезны в определенных случаях использования. Давайте рассмотрим их немного подробнее.
4.1. Суперкласс toString()
По умолчанию выходные данные не содержат данных из реализации суперкласса метода toString()
. Однако мы можем изменить это, установив для атрибута callSuper
значение true :
@ToString(callSuper = true)
public class SavingAccount extends Account {
private String savingAccountId;
// standard getters and setters
}
Это дает следующий вывод с информацией о суперклассе, за которой следуют поля и значения подкласса:
SavingAccount(super=Account(id=12345, name=An account), savingAccountId=6789)
Важно отметить, что это действительно полезно только тогда, когда мы расширяем класс, отличный от java.lang.Object
. Объектная реализация toString
()
не предоставляет много полезной информации. Другими словами, включение этих данных только добавляет избыточную информацию, а также увеличивает уровень детализации вывода.
4.2. Пропуск имен полей
Как мы видели ранее, выходные данные по умолчанию содержат имена полей, за которыми следуют значения. Однако мы можем опустить имена полей из вывода, установив для атрибута includeFieldNames значение
false
в аннотации @ToString
:
@ToString(includeFieldNames = false)
public class Account {
private String id;
private String name;
// standard getters and setters
}
В результате вывод теперь показывает список всех значений полей, разделенных запятыми, без имен полей:
Account(12345, An account)
4.3. Использование полей вместо геттеров
Как мы уже видели, методы получения предоставляют значения полей для печати. Кроме того, если класс не содержит метода получения для определенного поля, Lombok напрямую обращается к полю и получает его значение.
Однако мы можем настроить Lombok так, чтобы он всегда использовал значения прямого поля, а не геттеры, установив для атрибута doNotUseGetters значение
true
:
@ToString(doNotUseGetters = true)
public class Account {
private String id;
private String name;
// ignored getter
public String getId() {
return "this is the id:" + id;
}
// standard getters and setters
}
Без этого атрибута мы бы получили результат, вызванный геттерами:
Account(id=this is the id:12345, name=An account)
Вместо этого с атрибутом doNotUseGetters
выходные данные фактически показывают значение поля id
без вызова метода получения :
Account(id=12345, name=An account)
4.4. Включение и исключение полей
Допустим, мы хотим исключить определенные поля из строкового представления, например, пароли, другую конфиденциальную информацию или большие структуры JSON. Мы можем опустить такие поля, просто пометив их аннотацией @ ToString.Exclude
.
Исключим поле name
из нашего представления:
@ToString
public class Account {
private String id;
@ToString.Exclude
private String name;
// standard getters and setters
}
В качестве альтернативы мы можем указать только те поля, которые необходимы для вывода . Давайте сделаем это, используя @ToString(onlyExplicitlyIncluded = true)
на уровне класса, а затем аннотируя каждое обязательное поле с помощью @ToString.Include
:
@ToString(onlyExplicitlyIncluded = true)
public class Account {
@ToString.Include
private String id;
private String name;
// standard getters and setters
}
Оба приведенных выше подхода производят следующий вывод только с полем id :
Account(id=12345)
Кроме того, вывод Lombok автоматически исключает все переменные, начинающиеся с символа $
. Однако мы можем переопределить это поведение и включить их, добавив аннотацию @ToString.Include
на уровне поля.
4.5. Заказ вывода
По умолчанию выходные данные содержат поля в соответствии с порядком объявления в классе. Однако мы можем настроить порядок, просто добавив атрибут rank в аннотацию @ToString.Include
.
Давайте изменим наш класс Account
, чтобы поле id
отображалось перед любыми другими полями, независимо от позиции объявления в определении класса. Мы можем добиться этого, добавив аннотацию @ToString.Include(rank = 1) в поле
id
:
@ToString
public class Account {
private String name;
@ToString.Include(rank = 1)
private String id;
// standard getters and setters
}
Теперь поле id
отображается первым в выводе, несмотря на его объявление после поля имени :
Account(id=12345, name=An account)
Выходные данные сначала содержат элементы с более высоким рангом, а затем с более низким рангом. Значение ранга по умолчанию для элементов без атрибута ранга равно 0. Члены с одинаковым рангом печатаются в соответствии с порядком их объявления.
4.6. Вывод метода
В дополнение к полям также можно включить вывод метода экземпляра, который не принимает аргументов. Мы можем сделать это, пометив метод экземпляра без аргументов с помощью @ToString.Include
:
@ToString
public class Account {
private String id;
private String name;
@ToString.Include
String description() {
return "Account description";
}
// standard getters and setters
}
Это добавляет описание
в качестве ключа и его вывод в качестве значения к представлению Пользователя
:
Account(id=12345, name=An account, description=Account description)
Если указанное имя метода совпадает с именем поля, то метод имеет приоритет над полем . Другими словами, выходные данные содержат результат вызова метода вместо соответствующего значения поля.
4.7. Изменение имен полей
Мы можем изменить любое имя поля, указав другое значение в атрибуте имени аннотации
@ ToString.Include :
@ToString
public class Account {
@ToString.Include(name = "identification")
private String id;
private String name;
// standard getters and setters
}
Теперь вывод содержит альтернативное имя поля из атрибута аннотации вместо фактического имени поля:
Account(identification=12345, name=An account)
5. Печать массивов
Массивы печатаются с помощью метода Arrays.deepToString()
. Это преобразует элементы массива в соответствующие строковые представления . Однако возможно, что массив содержит либо прямую ссылку, либо косвенную циклическую ссылку.
Чтобы избежать бесконечной рекурсии и связанных с ней ошибок времени выполнения, этот метод отображает любые циклические ссылки на массив изнутри себя как «[[…]]».
Давайте посмотрим на это, добавив поле массива Object
в наш класс Account :
@ToString
public class Account {
private String id;
private Object[] relatedAccounts;
// standard getters and setters
}
Массив relatedAccounts
теперь включен в вывод:
Account(id=12345, relatedAccounts=[54321, [...]])
Важно отметить, что циклическая ссылка обнаруживается методом deepToString()
и корректно отображается Lombok, не вызывая StackOverflowError
.
6. Что нужно помнить
Стоит упомянуть несколько деталей, которые важны, чтобы избежать неожиданных результатов.
При наличии в классе любого метода с именем toString()
(независимо от типа возвращаемого значения) Lombok не генерирует свой метод toString()
.
Различные версии Lombok могут изменить формат вывода сгенерированного метода . В любом случае нам следует избегать кода, основанного на разборе вывода метода toString() .
Так что это не должно быть проблемой.
Наконец, мы также можем добавить эту аннотацию к enum
s . Это создает представление, в котором значение перечисления
следует за именем класса перечисления , например,
AccounType.SAVING
.
7. Заключение
В этой статье мы увидели, как использовать аннотации Lombok для создания строкового
представления объектов Java с минимальными усилиями и шаблоном.
Сначала мы рассмотрели базовое использование, которого обычно достаточно для большинства случаев. Затем мы рассмотрели широкий спектр опций, доступных для настройки и настройки сгенерированного вывода.
Как всегда, полный исходный код доступен на GitHub .