Ответ
Чтобы класс был полностью иммутабельным, необходимо соблюдать следующие правила:
- Объявите класс как
final. Это предотвращает создание подклассов, которые могли бы изменить поведение или состояние. - Объявите все поля как
private final. Поля инициализируются только один раз — в конструкторе. - Не предоставляйте сеттеров (setter methods). Отсутствие методов, меняющих состояние, — основа иммутабельности.
- Защищайте изменяемые поля. Если класс содержит ссылки на изменяемые объекты (например,
List,Map, массивы):- В конструкторе: Не сохраняйте переданную извне ссылку. Создавайте и сохраняйте глубокую копию (deep copy) или используйте защитную копию (defensive copy).
- В геттерах: Не возвращайте прямую ссылку на внутреннее поле. Возвращайте копию поля или неизменяемое представление (например, через
Collections.unmodifiableList()).
Пример корректного иммутабельного класса:
import java.util.*;
public final class ImmutablePerson {
private final String name;
private final List<String> hobbies; // Изменяемый объект!
// Конструктор с защитным копированием
public ImmutablePerson(String name, List<String> hobbies) {
this.name = name;
// Создаем новый ArrayList из переданного списка
this.hobbies = new ArrayList<>(hobbies);
}
public String getName() {
return name;
}
// Геттер, возвращающий неизменяемое представление
public List<String> getHobbies() {
return Collections.unmodifiableList(hobbies);
}
}
// Использование
List<String> originalHobbies = new ArrayList<>(Arrays.asList("Reading", "Cycling"));
ImmutablePerson person = new ImmutablePerson("Alice", originalHobbies);
originalHobbies.add("Gaming"); // Не влияет на состояние person
person.getHobbies().add("Swimming"); // Выбросит UnsupportedOperationException