JavaRush /Курси /JAVA 25 SELF /Помилки з модифікаторами доступу

Помилки з модифікаторами доступу

JAVA 25 SELF
Рівень 23 , Лекція 2
Відкрита

1. Вступ

У Java модифікатори доступу — це як система замків у домі. Вони визначають, хто й звідки може «зайти» до вас у кімнату (або до поля/методу вашого класу). Якщо всі двері відчинені — будь-хто може прийти й щось змінити. Якщо все зачинено — ніхто нічого не зламає, але й ви самі в якийсь момент можете опинитися зачиненими всередині.

Нагадаємо, у Java є чотири основні рівні доступу:

Модифікатор Доступний усередині класу Доступний у пакеті Доступний у підкласах Доступний в інших пакетах
private
(package)
protected
(через наслідування)
public

(package) — це коли модифікатор не вказано явно. Такий член класу видимий лише всередині одного пакета.

2. Типові помилки з модифікаторами доступу

Помилка 1: Поля й методи з модифікатором за замовчуванням (package-private)

Найпоширеніша помилка новачків — забути вказати модифікатор доступу. Унаслідок цього поле або метод стає доступним у всьому пакеті, хоча ви цього не планували. Це може призвести до того, що інший клас (у тому ж пакеті, але не повʼязаний із вашим) зможе змінити внутрішній стан вашого обʼєкта.

// Помилка: поле name не захищено!
class User {
    String name; // package-private!
}

Унаслідок цього будь-який клас із цього пакета може написати:

User user = new User();
user.name = "Василь"; // без обмежень!

Помилка 2: Порушення інкапсуляції — відкриті поля (public)

Друга за популярністю помилка — оголошувати поля класу як public. Це зручно, коли ви тільки навчаєтеся або пишете короткий приклад, але в реальних проєктах це майже завжди погано. Ви втрачаєте контроль над тим, хто і як змінює ваші дані.

public class Account {
    public double balance; // НЕБЕЗПЕЧНО!
}

Тепер будь-який код може зробити:

Account acc = new Account();
acc.balance = -1000000; // І хто тепер винен?

Помилка 3: Відсутність гетерів і сетерів

Іноді розробник робить поля private, але забуває додати методи для керування ними. У результаті отримати або змінити значення неможливо навіть там, де це було б доречно.

public class Product {
    private String name;
    // Немає ані getName(), ані setName()
}

Помилка 4: Спроба доступу до private-членів з іншого класу

Якщо ви оголосили поле або метод як private, то звернутися до нього не можна з іншого класу, навіть якщо він у тому самому пакеті. Новачки часто дивуються, чому поле «не видно».

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Помилка компіляції!
    }
}

Помилка 5: Помилки з protected

Чимало хто вважає, що protected означає «видно всюди, де є наслідування». Але в Java доступ до protected-членів поза пакетом можливий лише через наслідування й тільки для підкласу. Це тонкість, яку легко проґавити.

package animals;

public class Animal {
    protected void sleep() {}
}

package zoo;
import animals.Animal;

public class Dog extends Animal {
    public void test() {
        sleep(); // OK — нащадок
    }
}

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Помилка: не нащадок!
    }
}

3. Як правильно: найкращі практики

Правило 1: За замовчуванням робіть поля private

Це головний принцип інкапсуляції. Поля мають бути приховані від усіх, окрім самого класу. Якщо потрібно дати доступ — використовуйте гетери й сетери.

public class Book {
    private String title;
    private int pages;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Правило 2: Відкривайте лише необхідні методи

Якщо метод має бути доступний ззовні — робіть його public. Якщо потрібен лише всередині пакета — залишайте package-private. Якщо метод призначений тільки для нащадків — використовуйте protected.

Правило 3: Мінімізуйте область видимості

Що менша область видимості, то менша ймовірність випадкових помилок і «несподіваних гостей». Не робіть методи та поля public, якщо в цьому немає потреби.

Правило 4: Використовуйте гетери й сетери для контролю доступу

Це дає змогу додати додаткову логіку під час читання/запису поля, наприклад, валідацію.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Баланс не може бути відʼємним!");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Правило 5: Не відкривайте внутрішню реалізацію

Якщо у вас є масив або список як поле, не повертайте його напряму через гетер — повертайте копію або надавайте лише потрібні методи.

public class Team {
    private List<String> members = new ArrayList<>();

    // Правильно:
    public List<String> getMembers() {
        return new ArrayList<>(members); // повертаємо копію
    }
}

4. Приклади на практиці

Припустімо, у нас є клас LibraryUser, який описує користувача бібліотеки.

Приклад неправильної реалізації

public class LibraryUser {
    public String name;
    public int borrowedBooks;
}

У такому вигляді будь-який код може зробити з обʼєктом усе, що завгодно:

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Логіка? Яка логіка?

Приклад правильної реалізації з інкапсуляцією

public class LibraryUser {
    private String name;
    private int borrowedBooks;

    public LibraryUser(String name) {
        this.name = name;
        this.borrowedBooks = 0;
    }

    public String getName() {
        return name;
    }

    public int getBorrowedBooks() {
        return borrowedBooks;
    }

    public void borrowBook() {
        borrowedBooks++;
    }

    public void returnBook() {
        if (borrowedBooks > 0) {
            borrowedBooks--;
        }
    }
}

Тепер зовнішній код не може безпосередньо змінити кількість взятих книжок або імʼя користувача. Усе контролюється лише через методи класу.

5. Особливості та нюанси реалізації

Іноді здається, що простіше зробити поле public, ніж писати купу гетерів і сетерів. Але це пастка! Відкрите поле — це як відчинені двері у квартиру: так, зручно, але не дуже безпечно.

Ще одна тонкість — не завжди потрібно робити гетери й сетери для всіх полів. Якщо значення поля не має змінюватися після створення обʼєкта, робіть лише гетер, а поле — final:

public class Passport {
    private final String number;

    public Passport(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

Також памʼятайте: якщо клас оголошено як public, імʼя файлу має збігатися з іменем класу! Це не зовсім про модифікатори доступу, але дуже часта помилка новачків.

6. Типові помилки під час роботи з модифікаторами доступу

Помилка № 1: забули вказати модифікатор доступу для поля або методу. Унаслідок цього поле або метод стає доступним у всьому пакеті, навіть якщо ви цього не хотіли. Завжди явно вказуйте модифікатор, навіть якщо IDE не попереджає.

Помилка № 2: усі поля оголошено як public. Це руйнує інкапсуляцію, робить ваш код уразливим і непередбачуваним. Звичка з прикладів «для простоти» не має переходити в робочий код.

Помилка № 3: спроба звернутися до private-поля з іншого класу. Java цього не дозволить — компілятор вас захистить, але якщо ви раптом захотіли «обійти» це через рефлексію — замисліться, чому взагалі виникла така потреба.

Помилка № 4: очікування, що protected-члени будуть доступні всюди, де є наслідування. Насправді поза пакетом до них можна звернутися лише з підкласу й тільки через this або через обʼєкт підкласу.

Помилка № 5: повернення внутрішньої колекції через гетер. Якщо повернути посилання на внутрішній масив або список, зовнішній код зможе його змінити, порушуючи інваріанти класу.

Помилка № 6: відсутність контролю під час встановлення значення через сетер. Якщо не перевірити вхідне значення, можна отримати некоректний стан обʼєкта (наприклад, відʼємний баланс).

Помилка № 7: надто широка область видимості методів. Іноді методи роблять public, хоча вони потрібні лише всередині пакета або класу. Це відкриває зайвий API і ускладнює підтримку.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ