Ответ
Не обязательно. Этого набора условий недостаточно для гарантии полной неизменяемости (immutability). Критически важный недостающий элемент — защита изменяемых (mutable) полей.
Полный список требований к неизменяемому классу:
- Класс объявлен как
final. - Все поля объявлены как
private final. - Отсутствуют сеттеры (setter methods).
- Все поля инициализируются в конструкторе.
- Защита mutable-полей: Любые поля, являющиеся ссылками на изменяемые объекты (массивы, коллекции, другие mutable-классы), должны быть защищены:
- При сохранении: Не хранить переданную извне ссылку. Вместо этого выполнять защитное копирование (defensive copy) в новый объект.
- При возврате: Не возвращать прямую ссылку на внутреннее поле. Возвращать либо копию, либо неизменяемую обертку (например,
Collections.unmodifiableList()).
Пример уязвимого "псевдо-неизменяемого" класса:
public final class VulnerablePerson {
private final String name;
private final List<String> hobbies; // Mutable поле!
// ОШИБКА: сохраняем прямую ссылку на внешний список
public VulnerablePerson(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies; // Опасность!
}
// ОШИБКА: возвращаем прямую ссылку на внутренний список
public List<String> getHobbies() {
return hobbies; // Опасность!
}
}
// Вызывающий код может изменить список после создания объекта.