Чи можна наслідуватися від незмінного класу?
Технічно — так, якщо клас не позначено як final. Але це погана ідея, тому що підклас може додати змінювані поля, і тоді код, що очікує незмінний об'єкт, отримає мутабельний.
Junior Level
Технічно — так, якщо клас не позначено як final. Але це погана ідея, тому що підклас може додати змінювані поля, і тоді код, що очікує незмінний об’єкт, отримає мутабельний.
Приклад проблеми
// "Незмінний" клас, але без final
public class ImmutableBase {
private final String value;
public ImmutableBase(String v) { this.value = v; }
public String getValue() { return value; }
}
// Підклас додає мутабельність
public class MutableChild extends ImmutableBase {
private String extra; // мутабельне поле
public void setExtra(String e) { this.extra = e; }
}
Рішення
Завжди оголошуйте незмінні класи як final:
public final class ImmutablePoint { ... } // наслідування неможливе
Middle Level
Чому наслідування ламає незмінність
- Нові мутабельні поля — підклас може додати
private String extraз сетером - Перевизначення методів — підклас може перевизначити гетери, повертаючи інші дані
- Нестабільний hashCode — незмінні об’єкти часто кешують hashCode; підклас із мутабельними полями робить цей кеш невалідним.
Альтернативи наслідуванню
1. Композиція замість наслідування
public final class ExtendedPoint {
private final ImmutablePoint point;
private final String label;
// ...
}
2. Приватні конструктори + статичні фабрики
public final class ImmutableObject {
private final String data;
private ImmutableObject(String data) { this.data = data; }
public static ImmutableObject of(String data) { return new ImmutableObject(data); }
}
Приклади з JDK
Усі фундаментальні незмінні класи — final: String, Integer, BigDecimal, LocalDate.
Senior Level
Поліморфізм як загроза
Якщо метод очікує ImmutableBase і покладається на його незмінність (наприклад, кешує результат на основі getValue()), підстановка MutableChild може призвести до:
- Порушення інваріантів — дані зміняться після перевірки
- Security bypass — підклас поверне інші дані після валідації
- Втрати потокобезпеки — підклас звертається до не-
finalполів без синхронізації
Sealed Classes (preview у Java 15, stable у Java 17)
Якщо потрібне обмежене наслідування:
public sealed class Shape permits Circle, Rectangle {
// Обидва permits-класи теж повинні бути незмінними
}
Резюме для Senior
- У більшості випадків наслідування від незмінного класу — антипатерн. Виняток: sealed class hierarchy (Java 17+), де всі нащадки також незмінні.
- Завжди позначайте незмінні класи як
final - Незмінність — це не лише
finalполя, а й заборона на зміну логіки через поліморфізм - Використовуйте Sealed classes для контрольованого наслідування в Java 17+
Шпаргалка для інтерв’ю
Обов’язково знати:
- Технічно можна (якщо не
final), але це антипатерн — підклас додасть мутабельність - Підклас може: додати мутабельні поля, перевизначити гетери, зробити hashCode нестабільним
- Рішення: завжди
final classдля незмінних класів - Альтернативи: композиція замість наслідування, sealed classes (Java 17+)
- Усі фундаментальні незмінні класи JDK — final: String, Integer, BigDecimal, LocalDate
- Sealed classes дозволяють обмежене наслідування з контролем нащадків
Часті уточнювальні запитання:
- Чому наслідування ламає незмінність? — Підклас додає мутабельні поля або перевизначає гетери
- Що якщо потрібно розширити функціонал? — Композиція: огорнути незмінний об’єкт у новий final клас
- Sealed classes vs final? — Sealed дозволяє конкретних нащадків, final забороняє всіх
- Поліморфізм як загроза? — Метод покладається на незмінність, підклас підміняє дані після перевірки
Червоні прапорці (НЕ говорити):
- «Наслідування від незмінного класу — нормально» — це антипатерн
- «Підклас не може змінити final поля» — вірно, але може перевизначити гетери
- «Sealed classes = фінал» — sealed дозволяє наслідування, але обмежене
- «Можна додати mutable поле в підклас» — це руйнує контракт незмінності
Пов’язані теми:
- [[16. Чому незмінний клас повинен бути final]]
- [[17. Що станеться, якщо перевизначити геттер в підкласі незмінного класу]]
- [[3. Як створити незмінний клас в Java]]
- [[7. Що таке ключове слово final і як воно допомагає у створенні незмінних класів]]