1. Что такое геттеры и сеттеры
Если представить объект как сейф, то его приватные поля — это содержимое сейфа, а геттеры и сеттеры — ключи к отдельным ячейкам. Геттеры позволяют узнать, что лежит внутри, а сеттеры — аккуратно положить туда что-то новое (но только если вы не кладёте туда, например, ёжика вместо документов).
Геттер
Геттер — это публичный метод (public), который возвращает значение приватного поля (private). Обычно его имя начинается с get + имя поля с большой буквы.
public class Person {
private String name; // Приватное поле
// Геттер для поля name
public String getName() {
return name;
}
}
Сеттер
Сеттер — это публичный метод (public), который позволяет изменить значение приватного поля. Его имя начинается с set + имя поля с большой буквы.
public class Person {
private String name;
// Сеттер для поля name
public void setName(String name) {
this.name = name;
}
}
Для boolean-полей
Для полей типа boolean принято использовать префикс is в геттерах:
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
2. Синтаксис и соглашения об именовании
Java — язык строгий, но не зануда. Есть устоявшиеся соглашения, которые делают ваш код понятным для других разработчиков (и для вас самого через месяц).
- Геттер: public Type getИмяПоля()
- Сеттер: public void setИмяПоля(Type value)
- Геттер для boolean: public boolean isИмяПоля()
Имя поля в методе пишется с заглавной буквы: если поле age, то методы будут getAge() и setAge().
Это соглашение поддерживает JavaBeans-стиль, благодаря которому IDE, библиотеки и фреймворки могут автоматически находить ваши геттеры и сеттеры. Например, если вы используете Spring или JavaFX, эти методы будут «магически» вызываться, когда это потребуется.
3. Примеры кода
Давайте возьмём учебный проект — простое приложение «контактов» (аналог телефонной книги) — и добавим туда правильные геттеры и сеттеры.
Пример: Класс Contact с приватными полями и геттерами/сеттерами
public class Contact {
private String name;
private String phone;
private int age;
private boolean favorite;
// Геттеры
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public boolean isFavorite() {
return favorite;
}
// Сеттеры
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
// Пример валидации: возраст не должен быть отрицательным
if (age < 0) {
System.out.println("Возраст не может быть отрицательным!");
return;
}
this.age = age;
}
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
}
Использование в приложении
Contact friend = new Contact();
friend.setName("Иван Иванов");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);
System.out.println("Имя: " + friend.getName());
System.out.println("Телефон: " + friend.getPhone());
System.out.println("Возраст: " + friend.getAge());
System.out.println("Избранный: " + (friend.isFavorite() ? "Да" : "Нет"));
Пример валидации в сеттере
Обратите внимание, что в сеттере setAge мы добавили простую проверку: если возраст отрицательный, мы не изменяем поле и выводим предупреждение. Это простой способ защитить объект от некорректных данных.
4. Best practices: как делать правильно
Не все поля должны иметь публичные сеттеры
Иногда поле должно быть только для чтения — например, уникальный идентификатор, который задаётся при создании объекта и больше не меняется. В таком случае сеттер просто не пишут:
public class Contact {
private final int id; // final — нельзя изменить после инициализации
public Contact(int id) {
this.id = id;
}
public int getId() {
return id;
}
// Нет setId!
}
Используйте геттеры/сеттеры для контроля доступа и валидации
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("Имя не может быть пустым!");
return;
}
this.name = name;
}
Не раскрывайте внутренние изменяемые объекты напрямую
Если у вас есть поле — например, список телефонов:
private String[] phones;
Не стоит возвращать его напрямую через геттер:
public String[] getPhones() {
return phones; // Плохо!
}
Такой код позволяет внешнему коду менять список как угодно — нарушая инкапсуляцию!
Правильнее: возвращать копию массива:
public String[] getPhones() {
return Arrays.copyOf(phones, phones.length); // Возвращаем копию
}
Или просто клонировать:
public String[] getPhones() {
return phones.clone();
}
Делайте геттеры и сеттеры понятными и простыми
- Не пишите сложную бизнес-логику в геттерах/сеттерах — их задача простая: контроль доступа и, по необходимости, валидация.
- Если поле не должно меняться — не делайте сеттер вообще.
- Если поле не должно быть доступно снаружи — не делайте геттер.
5. Автоматическая генерация геттеров/сеттеров в IDE
Писать аксессоры вручную — то ещё удовольствие, особенно если у класса десяток полей. К счастью, современные IDE (например, IntelliJ IDEA, Eclipse, VS Code с плагинами) умеют генерировать их автоматически.
В IntelliJ IDEA
- Откройте класс, поставьте курсор внутри тела класса.
- Нажмите Alt + Insert (или Code -> Generate...).
- Выберите Getter and Setter.
- Отметьте нужные поля и нажмите OK.
Вуаля! Ваши геттеры и сеттеры появятся как по волшебству.
В Eclipse
- Откройте класс.
- Правая кнопка мыши — Source — Generate Getters and Setters...
- Выберите поля и нажмите OK.
В VS Code (с Java Extension Pack)
- Откройте файл класса.
- В палитре команд (Ctrl+Shift+P) наберите Generate getters and setters.
- Следуйте подсказкам.
6. Развитие вашего приложения: инкапсуляция в действии
В предыдущих лекциях вы строили простое приложение для хранения контактов. Теперь мы можем улучшить его, сделав поля приватными и предоставив доступ только через геттеры/сеттеры.
Было (плохой пример):
public class Contact {
public String name;
public String phone;
public int age;
}
Проблема: любой код может сделать так:
Contact c = new Contact();
c.age = -1000; // Теперь у нас вампир в телефонной книге!
Стало (хороший пример):
public class Contact {
private String name;
private String phone;
private int age;
public void setAge(int age) {
if (age < 0) {
System.out.println("Возраст не может быть отрицательным!");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
// Остальные геттеры/сеттеры...
}
Теперь невозможно случайно (или специально) сломать объект снаружи.
7. Геттеры/сеттеры для вычисляемых и неизменяемых свойств
Иногда значение не хранится в поле, а вычисляется на лету:
public class Rectangle {
private int width;
private int height;
public int getArea() {
return width * height;
}
}
Сеттер для площади не нужен — её нельзя установить напрямую, только изменить ширину или высоту.
8. Геттеры и сеттеры: типичные ошибки
Ошибка №1: Геттер/сеттер нарушает инкапсуляцию.
Если геттер возвращает ссылку на внутренний изменяемый объект (например, на список), внешний код может его изменить, обходя все проверки. Это подрывает идею инкапсуляции.
Ошибка №2: Сеттер не валидирует данные.
Если сеттер просто присваивает значение, не проверяя его, можно получить некорректное состояние объекта (например, отрицательный возраст или пустое имя).
Ошибка №3: Автоматическая генерация сеттеров для всех полей.
IDE может сгенерировать сеттеры для всех полей, но это не всегда правильно! Например, для идентификатора (id) сеттер не нужен.
Ошибка №4: Сложная логика в геттерах/сеттерах.
Геттеры и сеттеры должны быть простыми. Если в них появляется сложная бизнес-логика, стоит вынести её в отдельные методы.
Ошибка №5: Нарушение соглашений об именовании.
Если назвать геттер fetchName() вместо getName(), некоторые фреймворки и библиотеки не смогут его распознать.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ