Lombok: как отключить автоматическую генерацию геттеров и сеттеров
Для кого эта статья:
- Java-разработчики, которые хотят улучшить свой код и снизить его объем с помощью Lombok
- Специалисты, работающие с конфиденциальными данными и требованиями к инкапсуляции
Студенты и новички, изучающие Java и желающие освоить современные инструменты разработки
Lombok — это бесценный помощник Java-разработчика, избавляющий нас от тонн шаблонного кода. Но что делать, когда нужен полный контроль над доступом к полям? 🔍 Представьте ситуацию: у вас класс с десятком полей, для которых
@Getterи@Setterработают отлично, но есть одно-два поля, требующие особого обращения. Настройка селективного генерирования методов доступа — это не просто вопрос чистоты кода, это вопрос защиты данных и соблюдения инкапсуляции. Разберемся, как именно Lombok позволяет тонко настраивать доступ к каждому полю вашего класса.
Изучаете Java и хотите писать чистый, лаконичный код? На Курсе Java-разработки от Skypro вы не только освоите Lombok и другие инструменты профессиональной разработки, но и научитесь создавать высокопроизводительные приложения. Наши выпускники уже через 9 месяцев применяют продвинутые техники оптимизации кода в реальных проектах. Присоединяйтесь — и пишите код, которым действительно можно гордиться!
Базовые приемы отключения геттеров и сеттеров в Lombok
Работа с Lombok начинается с понимания его базовой философии — уменьшение шаблонного кода. Однако контроль над генерацией методов доступа остается в руках разработчика. Рассмотрим основные способы выборочного отключения геттеров и сеттеров.
Самый простой способ избежать генерации геттера или сеттера для конкретного поля — использовать аннотацию с параметром уровня доступа:
@Getter
@Setter
public class User {
private String username;
private String email;
@Getter(AccessLevel.NONE) // Геттер не будет сгенерирован
private String password;
}
В приведенном примере Lombok сгенерирует методы доступа для полей username и email, но опустит создание геттера для поля password, обеспечивая дополнительный уровень защиты чувствительных данных.
Lombok предлагает различные уровни доступа через перечисление AccessLevel:
PUBLIC— стандартный уровень доступа (по умолчанию)PROTECTED— для методов, доступных только в пакете и подклассахPACKAGE— для методов с доступом на уровне пакетаPRIVATE— для методов, доступных только внутри классаNONE— полное исключение генерации метода
Когда требуется более точный контроль, можно применять аннотации индивидуально к каждому полю:
public class Configuration {
@Getter // Только геттер
private String serverName;
@Setter // Только сеттер
private int maxConnections;
// Без автоматически генерируемых методов доступа
private boolean debugMode;
}
Этот подход особенно полезен при создании конфигурационных классов или классов данных с различными требованиями к доступу для разных полей.
| Метод исключения | Применимость | Пример использования |
|---|---|---|
| Явное указание AccessLevel.NONE | Для отдельных полей при общей аннотации класса | @Getter(AccessLevel.NONE) private String secretKey; |
| Отсутствие аннотации на поле | Когда аннотации применяются к каждому полю отдельно | private int internalCounter; // Без аннотаций |
| Явное создание ручного метода | Для полей с особой логикой доступа | public String getFormattedName() { return name.toUpperCase(); } |
Алексей Петров, Tech Lead
Наша команда разрабатывала систему управления финансовыми транзакциями, где безопасность данных была критически важна. Мы активно использовали Lombok для уменьшения объема кода, но столкнулись с проблемой: некоторые поля, такие как платежные токены и ключи шифрования, требовали особого обращения.
Изначально я применял аннотации
@Getterи@Setterна уровне класса, что привело к нежелательной доступности конфиденциальных полей. Решение пришло, когда я обнаружил возможность использоватьAccessLevel.NONE:JavaСкопировать код@Getter @Setter public class PaymentTransaction { private Long id; private BigDecimal amount; @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) private String securityToken; // Специализированный метод доступа с логированием public String getSecurityToken(User requestingUser) { log.info("Token access requested by: {}", requestingUser.getId()); // Дополнительная проверка прав доступа return securityToken; } }Это позволило нам сохранить краткость кода благодаря Lombok, одновременно обеспечив контроль доступа к чувствительным данным. После этого случая избирательное применение
AccessLevel.NONEстало стандартной практикой в нашей команде для всех классов с данными разного уровня конфиденциальности.

Применение AccessLevel.NONE для избирательной генерации
AccessLevel.NONE — это мощный инструмент, позволяющий точечно контролировать генерацию методов доступа в Lombok. Давайте рассмотрим различные сценарии его применения для создания более безопасных и чистых классов.
При работе с классами, имеющими смешанные требования к доступу, AccessLevel.NONE становится незаменимым. Рассмотрим более сложный пример:
@Getter
@Setter
public class BankAccount {
private String accountNumber;
private String ownerName;
@Getter(AccessLevel.NONE) // Блокируем прямой доступ к балансу
private BigDecimal balance;
@Setter(AccessLevel.NONE) // Идентификатор нельзя изменять после создания
private UUID accountId = UUID.randomUUID();
// Специальный метод с проверкой и аудитом
public BigDecimal getBalance(User requestingUser) {
// Проверка прав доступа и аудит
return balance;
}
// Методы для изменения баланса с бизнес-логикой
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
this.balance = this.balance.add(amount);
}
public void withdraw(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Withdrawal amount must be positive");
}
if (this.balance.compareTo(amount) < 0) {
throw new InsufficientFundsException("Insufficient funds");
}
this.balance = this.balance.subtract(amount);
}
}
В этом примере мы:
- Блокируем прямой доступ к полю
balanceчерез стандартный геттер - Запрещаем изменение
accountIdпосле создания объекта - Предоставляем специальные методы с бизнес-логикой для контролируемого изменения баланса
Применение AccessLevel.NONE особенно эффективно в следующих случаях:
- Защита чувствительных данных — предотвращение несанкционированного доступа к конфиденциальной информации
- Обеспечение бизнес-правил — гарантия того, что изменение данных происходит только через методы с проверками
- Поддержка неизменяемости — запрет модификации определенных полей после инициализации
- Упрощение API — сокрытие внутренних деталей реализации от пользователей класса
Иногда возникает необходимость в более тонкой настройке доступа — например, когда нужно сгенерировать метод, но с другим уровнем доступа:
public class AuditRecord {
@Getter // Публичный геттер
@Setter(AccessLevel.NONE) // Сеттер не генерируется
private final Instant timestamp = Instant.now();
@Getter(AccessLevel.PACKAGE) // Геттер с доступом на уровне пакета
@Setter(AccessLevel.PACKAGE) // Сеттер с доступом на уровне пакета
private String internalNote;
@Getter // Публичный геттер
@Setter(AccessLevel.PROTECTED) // Защищенный сеттер
private AuditStatus status = AuditStatus.CREATED;
}
Это позволяет создать API, точно соответствующий требованиям дизайна системы и обеспечивающий правильную инкапсуляцию данных.
| Сценарий использования | Рекомендуемая настройка | Преимущества подхода |
|---|---|---|
| Иммутабельные поля | @Getter + @Setter(AccessLevel.NONE) | Гарантия неизменности после инициализации |
| Поля только для внутреннего использования | @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) | Полное сокрытие внутренних деталей реализации |
| Поля с особой логикой доступа | @Getter(AccessLevel.NONE) + ручной метод с бизнес-логикой | Контролируемый доступ с соблюдением бизнес-правил |
| Поля для ограниченного внутреннего использования | @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) | Доступность только для классов в том же пакете |
Тонкая настройка аннотаций @Getter и @Setter для полей
Lombok предоставляет возможности для тонкой настройки генерации методов доступа на уровне отдельных полей. Это позволяет адаптировать поведение класса к специфическим требованиям проекта без написания лишнего кода.
Когда требуется особая логика при получении или установке значения поля, можно использовать комбинацию Lombok-аннотаций и ручных методов:
public class Product {
@Getter // Стандартный геттер
private String name;
@Setter(AccessLevel.NONE) // Отключаем стандартный сеттер
private BigDecimal price;
// Ручной сеттер с валидацией
public void setPrice(BigDecimal newPrice) {
if (newPrice == null || newPrice.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Price must be positive");
}
this.price = newPrice;
}
// Lombok не переопределит этот метод
public BigDecimal getPrice() {
return price.setScale(2, RoundingMode.HALF_UP);
}
}
В этом примере мы:
- Используем стандартный Lombok-геттер для поля
name - Отключаем автогенерацию сеттера для
priceс помощьюAccessLevel.NONE - Реализуем собственный сеттер с проверкой корректности цены
- Предоставляем ручной геттер, который возвращает цену, округленную до двух знаков
Lombok распознаёт существующие методы и не будет генерировать дубликаты, что позволяет безопасно комбинировать автоматическую и ручную генерацию.
Для полей с особыми требованиями к формату или представлению можно также использовать сочетание аннотаций и дополнительных методов:
public class Employee {
@Getter @Setter
private String firstName;
@Getter @Setter
private String lastName;
@Getter(AccessLevel.NONE) // Отключаем стандартный геттер
private LocalDate birthDate;
// Стандартный сеттер для birthDate генерируется автоматически
// Специализированные методы доступа к возрасту и дате рождения
public LocalDate getBirthDate() {
return birthDate;
}
public int getAge() {
return birthDate != null
? Period.between(birthDate, LocalDate.now()).getYears()
: 0;
}
// Дополнительный метод для полного имени
public String getFullName() {
return firstName + " " + lastName;
}
}
Такой подход позволяет одновременно использовать преимущества Lombok для снижения шаблонного кода и при этом расширять функциональность класса специализированными методами.
Михаил Соколов, Senior Java Developer
В проекте по разработке медицинской информационной системы мы столкнулись с проблемой при работе с данными пациентов. Нам требовалось обеспечить строгий контроль доступа к персональным данным, соблюдая требования законодательства о защите медицинской информации.
Мы использовали Lombok для сокращения кода, но обнаружили, что стандартные аннотации
@Getterи@Setterне позволяют достаточно тонко настроить доступ к различным категориям данных. Решение нашлось в комбинированном подходе:JavaСкопировать код@Getter @Setter public class PatientRecord { // Основные данные с обычным доступом private UUID patientId; private String firstName; private String lastName; // Медицинские данные с ограниченным доступом @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) private List<Diagnosis> diagnoses = new ArrayList<>(); @Getter(AccessLevel.NONE) private String medicalNotes; // Контролируемый доступ к медицинским данным public List<Diagnosis> getDiagnoses(Doctor requestingDoctor) { auditAccess(requestingDoctor, "diagnoses"); if (!hasAccess(requestingDoctor)) { throw new AccessDeniedException("No permission to view diagnoses"); } return Collections.unmodifiableList(diagnoses); } public void addDiagnosis(Doctor doctor, Diagnosis diagnosis) { auditAccess(doctor, "diagnoses:add"); if (!hasAccess(doctor)) { throw new AccessDeniedException("No permission to add diagnosis"); } this.diagnoses.add(diagnosis); } // Метод с расширенными проверками и аудитом public String getMedicalNotes(Doctor requestingDoctor) { auditAccess(requestingDoctor, "medicalNotes"); if (!isTreatingPhysician(requestingDoctor)) { throw new AccessDeniedException("Only treating physician can view medical notes"); } return medicalNotes; } public void setMedicalNotes(Doctor doctor, String notes) { auditAccess(doctor, "medicalNotes:update"); if (!isTreatingPhysician(doctor)) { throw new AccessDeniedException("Only treating physician can update notes"); } this.medicalNotes = notes; } // Вспомогательные методы проверки доступа... }Такой подход позволил нам использовать удобство Lombok для обычных полей, при этом обеспечив специализированный контроль доступа к чувствительным медицинским данным. Аудит действий и проверка прав были встроены непосредственно в методы доступа, что сделало невозможным обход системы контроля. Это решение прошло проверку на соответствие требованиям безопасности и стало стандартом в нашем проекте для всех классов с данными пациентов.
Сочетание аннотаций класса и полей для гибкого контроля
Lombok предоставляет возможность комбинировать аннотации на уровне класса и отдельных полей, что дает максимальную гибкость при определении поведения генерируемых методов доступа. Этот подход особенно полезен для крупных классов с разнородными полями.
Рассмотрим стратегию "по умолчанию для всех, исключения для избранных":
@Getter // Геттеры для всех полей по умолчанию
@Setter // Сеттеры для всех полей по умолчанию
public class UserProfile {
private String username;
private String email;
@Setter(AccessLevel.NONE) // Запрещаем изменение ID
private final UUID userId = UUID.randomUUID();
@Getter(AccessLevel.NONE) // Отключаем прямой доступ к паролю
private String password;
@Getter(AccessLevel.PROTECTED) // Ограниченный доступ к роли
@Setter(AccessLevel.PROTECTED) // Ограниченный доступ к изменению роли
private UserRole role;
// Специализированные методы для работы с паролем
public boolean verifyPassword(String inputPassword) {
return password != null && password.equals(inputPassword);
}
public void changePassword(String oldPassword, String newPassword) {
if (!verifyPassword(oldPassword)) {
throw new SecurityException("Old password is incorrect");
}
// Дополнительные проверки сложности пароля
this.password = newPassword;
}
}
В приведенном примере мы:
- Устанавливаем геттеры и сеттеры для всех полей по умолчанию
- Запрещаем сеттер для поля
userId, делая его эффективно неизменяемым - Отключаем стандартный геттер для
passwordи предоставляем метод проверки пароля - Ограничиваем доступ к полю
roleна уровнеprotected
Также можно использовать обратный подход — "запрещено всё, кроме разрешенного явно":
@Getter(AccessLevel.NONE) // По умолчанию геттеры не генерируются
@Setter(AccessLevel.NONE) // По умолчанию сеттеры не генерируются
public class SecureDocument {
@Getter // Явно разрешаем геттер
private final UUID documentId = UUID.randomUUID();
@Getter // Явно разрешаем геттер
private final Instant createdAt = Instant.now();
private String title;
private String content;
private DocumentStatus status = DocumentStatus.DRAFT;
// Методы доступа с проверками прав
public String getTitle(User user) {
verifyReadAccess(user);
return title;
}
public void setTitle(User user, String newTitle) {
verifyWriteAccess(user);
this.title = newTitle;
}
public String getContent(User user) {
verifyReadAccess(user);
return content;
}
public void setContent(User user, String newContent) {
verifyWriteAccess(user);
this.content = newContent;
}
@Getter(AccessLevel.PACKAGE) // Доступен только в пакете
public DocumentStatus getStatus() {
return status;
}
// Специализированные методы изменения статуса
public void publish(User user) {
verifyPublishAccess(user);
this.status = DocumentStatus.PUBLISHED;
}
// Вспомогательные методы проверки доступа...
}
В этом подходе мы:
- Отключаем генерацию всех методов доступа на уровне класса
- Избирательно включаем генерацию геттеров только для необходимых полей
- Реализуем специализированные методы доступа с проверками безопасности
- Используем методы с бизнес-семантикой вместо прямых сеттеров
Сочетание аннотаций на разных уровнях позволяет реализовать сложные стратегии доступа к данным, адаптированные к конкретным требованиям безопасности и бизнес-логики.
При работе с аннотациями Lombok важно помнить о порядке их применения:
- Аннотации на уровне поля всегда переопределяют аннотации на уровне класса
- Если для поля определен ручной метод доступа, Lombok не будет генерировать его автоматически
- Аннотации более конкретного уровня имеют приоритет над более общими
Это позволяет создавать точные и читаемые определения классов с минимальным объемом кода. 🔒
Практические решения для типичных сценариев использования
Рассмотрим конкретные примеры решения распространенных задач, связанных с избирательной генерацией геттеров и сеттеров в Lombok. Эти паттерны проверены практикой и могут быть адаптированы к специфике вашего проекта. 🧩
Сценарий 1: Классы с иммутабельными полями
Для создания классов с частично или полностью неизменяемыми полями:
@Getter
public class ImmutableEntity {
private final UUID id;
private final Instant createdAt;
@Setter // Только это поле можно изменять
private String description;
public ImmutableEntity(String description) {
this.id = UUID.randomUUID();
this.createdAt = Instant.now();
this.description = description;
}
}
Здесь поля id и createdAt инициализируются при создании и не имеют сеттеров, что делает их неизменяемыми, а поле description можно модифицировать.
Сценарий 2: Поля с валидацией при установке значений
@Getter
@Setter
public class ValidatedEntity {
private String name;
@Setter(AccessLevel.NONE) // Отключаем стандартный сеттер
private Integer age;
// Сеттер с валидацией
public void setAge(Integer age) {
if (age == null || age < 0 || age > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
this.age = age;
}
@Setter(AccessLevel.NONE) // Отключаем стандартный сеттер
private String email;
// Сеттер с валидацией формата email
public void setEmail(String email) {
if (email != null && !email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Invalid email format");
}
this.email = email;
}
}
В этом примере мы отключаем стандартные сеттеры для полей, требующих валидации, и предоставляем собственные методы с проверками.
Сценарий 3: Поля с ленивой инициализацией
@Getter
public class LazyLoadingEntity {
private final String id;
@Getter(AccessLevel.NONE) // Отключаем стандартный геттер
private List<RelatedEntity> relatedEntities;
public LazyLoadingEntity(String id) {
this.id = id;
}
// Геттер с ленивой инициализацией
public List<RelatedEntity> getRelatedEntities() {
if (relatedEntities == null) {
// Загружаем связанные сущности только при необходимости
relatedEntities = databaseService.loadRelatedEntities(id);
}
return Collections.unmodifiableList(relatedEntities);
}
}
Этот подход позволяет отложить дорогостоящие операции загрузки данных до момента их реального использования.
Сценарий 4: Классы с аудитом действий
@Getter
@Setter
public class AuditedEntity {
private String name;
@Getter(AccessLevel.NONE) // Отключаем стандартный геттер
@Setter(AccessLevel.NONE) // Отключаем стандартный сеттер
private BigDecimal balance;
// Геттер с аудитом
public BigDecimal getBalance(User user) {
auditService.logAccess(user, "balance:read", this);
return balance;
}
// Специализированные методы изменения баланса с аудитом
public void deposit(User user, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
auditService.logOperation(user, "balance:deposit", this, amount);
this.balance = this.balance.add(amount);
}
public void withdraw(User user, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
if (this.balance.compareTo(amount) < 0) {
throw new InsufficientFundsException("Insufficient funds");
}
auditService.logOperation(user, "balance:withdraw", this, amount);
this.balance = this.balance.subtract(amount);
}
}
Этот паттерн особенно полезен для финансовых или других чувствительных операций, где требуется отслеживание всех действий пользователей.
| Проблема | Решение с Lombok | Ключевые аннотации |
|---|---|---|
| Иммутабельность | @Getter + final поля + отсутствие @Setter | @Getter, @AllArgsConstructor |
| Валидация входных данных | @Setter(AccessLevel.NONE) + ручные сеттеры с проверками | @Getter, @Setter(AccessLevel.NONE) |
| Ленивая инициализация | @Getter(AccessLevel.NONE) + ручной геттер с проверкой null | @Getter(AccessLevel.NONE) |
| Аудит доступа | @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + специальные методы | @Getter(AccessLevel.NONE), @Setter(AccessLevel.NONE) |
| Разграничение прав | Геттеры/сеттеры с параметром пользователя и проверкой прав | @Getter(AccessLevel.NONE), @Setter(AccessLevel.NONE) |
Применяя эти шаблоны в своем коде, вы сможете сочетать удобство и краткость Lombok с гибкостью и безопасностью ручно написанных методов доступа, что приведет к созданию более надежных и поддерживаемых приложений. 🔐
Избирательная генерация геттеров и сеттеров с помощью Lombok — это не просто техническая особенность, а мощный инструмент проектирования API ваших классов. Правильное применение
AccessLevel.NONEи комбинирование аннотаций на разных уровнях позволяет создавать более безопасные, понятные и соответствующие принципам инкапсуляции классы, не жертвуя при этом краткостью кода. Помните: каждый публичный метод — это часть контракта вашего класса. Используйте представленные техники для точного контроля над тем, что вы действительно хотите предоставить пользователям ваших классов.