Immutable класс
Immutable (неизменяемый) класс — объект, состояние которого нельзя поменять после создания. Создал — и оно навсегда такое. String — самый известный пример из стандартной библиотеки.
Чтобы сделать свой класс неизменяемым, нужно выполнить четыре правила:
final class Point { // 1. класс final — нельзя унаследоваться и сломать гарантию
private final int x; // 2. поля final
private final int y;
Point(int x, int y) { // 3. значения только через конструктор
this.x = x;
this.y = y;
}
int getX() { return x; } // 4. только геттеры, без сеттеров
int getY() { return y; }
}
Если нужно “изменить” — создаёшь новый объект:
Point moved(Point p, int dx, int dy) {
return new Point(p.getX() + dx, p.getY() + dy); // новый объект, старый не тронут
}
Копнуть глубже
Главная ловушка — изменяемые поля внутри. Если поле само — изменяемый объект (например, List или Date), final на ссылке не спасает: содержимое можно поменять изнутри.
final class Team {
private final List<String> members;
Team(List<String> members) {
this.members = members; // ❌ опасно — хранится та же ссылка
}
List<String> getMembers() { return members; } // ❌ отдаёт прямой доступ
}
Team t = new Team(new ArrayList<>(List.of("Артур")));
t.getMembers().add("Хакер"); // 💥 изменили "неизменяемый" объект
Правильно — защитное копирование (defensive copy) на входе и выходе:
Team(List<String> members) {
this.members = List.copyOf(members); // копия при создании
}
List<String> getMembers() {
return List.copyOf(members); // копия при отдаче
}
Под капотом
Зачем вообще нужна неизменяемость:
- Потокобезопасность бесплатно. Несколько потоков могут читать один immutable-объект одновременно без блокировок — поменять его всё равно нельзя, гонки данных невозможны в принципе.
- Надёжность как ключ в
HashMap. Если объект — ключ коллекции, егоhashCodeне должен меняться после вставки, иначе запись “потеряется” (тот же принцип, что уString— см. тему «String»). - Предсказуемость. Передал объект в чужой метод — можешь быть уверен, что он не изменится у тебя за спиной.
Цена — каждое “изменение” создаёт новый объект, что может быть дороже по памяти, чем мутация на месте. Это сознательный компромисс: безопасность в обмен на лишние аллокации.
🎤 Закрыл тему, если можешь объяснить:
• четыре правила, чтобы сделать класс неизменяемым;
• что такое защитное копирование и зачем оно нужно (если дошёл до 2-го слоя).
• что такое защитное копирование и зачем оно нужно (если дошёл до 2-го слоя).