Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Инкапсуляция

Если любое место программы может записать в поле что угодно, объект легко сломать. Инкапсуляция — это правило «данные объекта меняет только сам объект». Снаружи доступны лишь методы, которые следят за корректностью.

Проблема открытых полей

Открытое поле не защищено от неверных значений. Никто не помешает сделать баланс отрицательным:

class Account {
    double balance;
}
 
void main() {
    Account account = new Account();
    account.balance = -1000;
    IO.println(account.balance);
}

Результат:

-1000.0

Модификатор private

Слово private перед полем закрывает его: поле видно только коду внутри класса. Работать с ним снаружи можно только через методы:

class Account {
    private double balance;
 
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
 
    double getBalance() {
        return balance;
    }
}
 
void main() {
    Account account = new Account();
    account.deposit(50);
    account.deposit(-20);
    IO.println(account.getBalance());
}

Результат:

50.0

Неверный взнос просто игнорируется: метод deposit() сам решает, что допустимо.

Геттеры и сеттеры

Метод, который возвращает значение поля, называют геттером (от англ. get — получить), а метод, который его задаёт, — сеттером (set — установить). По соглашению их называют getИмя и setИмя:

class User {
    private String name;
 
    String getName() {
        return name;
    }
 
    void setName(String name) {
        if (name.isBlank()) {
            throw new IllegalArgumentException("Имя не может быть пустым");
        }
        this.name = name;
    }
}
 
void main() {
    User user = new User();
    user.setName("Анна");
    IO.println(user.getName());
}

Результат:

Анна

Сеттер — не обязательная часть класса. Если поле не должно меняться снаружи, его просто не пишут.

Поля final

Поле с модификатором final присваивается один раз — в объявлении или в конструкторе — и больше не меняется. Так получают объекты, которые нельзя испортить после создания:

class Point {
    private final int x;
    private final int y;
 
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
 
    int getX() {
        return x;
    }
 
    int getY() {
        return y;
    }
}
 
void main() {
    Point p = new Point(3, 4);
    IO.println(p.getX() + ", " + p.getY());
}

Результат:

3, 4

Одна оговорка про примеры

В учебных примерах все классы лежат в одном файле, внутри неявного класса Main. Из-за этого компилятор разрешает main обращаться к private-полям — внутри одного внешнего класса закрытые члены видны. В настоящем проекте каждый класс живёт в своём файле, и строка вроде account.balance = -1000; вызывает ошибку balance has private access in Account. Как устроены такие проекты, разберём в уроке о пакетах.

Частая ошибка

Геттер, который возвращает внутренний список, открывает дверь для изменений в обход класса. Вызывающий код получает ссылку на тот же самый список:

class Order {
    private final List<String> items = new ArrayList<>();
 
    void add(String item) {
        items.add(item);
    }
 
    List<String> getItems() {
        return items;
    }
}
 
void main() {
    Order order = new Order();
    order.add("Чай");
    order.getItems().clear();
    IO.println(order.getItems());
}

Результат:

[]

Возвращайте неизменяемую копию — return List.copyOf(items); — и тогда вызов clear() снаружи выбросит исключение, а заказ останется целым.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.