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

Инкапсуляция объединяет данные с операциями и скрывает решения, которые клиенту не нужны. Практический результат — объект сам сохраняет свои инварианты.

public final class Account {
    private long balance;
 
    public void deposit(long amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("amount must be positive");
        }
        balance = Math.addExact(balance, amount);
    }
 
    public long balance() {
        return balance;
    }
}

Если сделать balance публичным или дать безусловный setter, любой клиент сможет записать отрицательное значение. Метод deposit выражает допустимую операцию, проверяет условие и скрывает представление.

Инкапсуляция не равна «все поля private»

class WeakModel {
    private List<String> items;
 
    public List<String> getItems() {
        return items; // внутреннее состояние всё равно раскрыто
    }
}

Нужны защитная копия, неизменяемое представление или операции уровня предметной области. Сокрытие данных — часть инкапсуляции, но важен и устойчивый контракт.

Границы

  • класс/объект;
  • пакет и package-private API;
  • модуль и экспортируемые пакеты;
  • сервис или компонент с протоколом.

Правильная граница помещает изменяемое решение за небольшим интерфейсом. Избыточные getters/setters превращают объект в структуру данных и переносят инварианты во все вызывающие места.

Ошибки

  • возвращать изменяемую внутреннюю коллекцию;
  • давать setter для каждого поля без бизнес-смысла;
  • прятать непредсказуемые побочные эффекты за безобидным getter;
  • считать reflection обычным способом обхода контракта.

На собеседовании

Инкапсуляция — это private? Это инструмент; цель — скрыть решение и защитить инварианты.

Почему setter может быть плох? Он разрешает изменение без контекста и проверки допустимой операции.

Как вернуть коллекцию безопасно? Неизменяемым представлением или защитной копией с учётом изменяемости элементов.

Связано: Модификаторы доступа, Immutable классы и данные, Основы ООП.