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. Найкращі практики: як робити правильно
Не для всіх полів потрібні публічні сетери
Іноді поле має бути лише для читання — наприклад, унікальний ідентифікатор, який присвоюється під час створення об’єкта і надалі не змінюється. У такому разі сетер просто не пишуть:
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(), деякі фреймворки та бібліотеки не зможуть його розпізнати.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ