Ответ
Чтобы сделать объект иммутабельным (неизменяемым), необходимо следовать набору строгих правил при проектировании его класса. Это гарантирует, что состояние объекта останется постоянным после создания.
Пошаговый алгоритм:
finalкласс: Объявите класс с модификаторомfinal, чтобы предотвратить создание подклассов, которые могут нарушить иммутабельность.private finalполя: Все поля должны быть объявлены какprivateиfinal. Это обеспечивает:- Инкапсуляцию (прямой доступ запрещен).
- Однократную инициализацию (значение присваивается только в конструкторе).
- Отсутствие сеттеров: Не предоставляйте методов, изменяющих состояние полей.
- Безопасная инициализация в конструкторе:
- Для примитивов и immutable-типов (например,
String,Integer) — присваивайте напрямую. - Для mutable-объектов и коллекций — создавайте защитные копии (defensive copy).
- Для примитивов и immutable-типов (например,
- Безопасное возвращение значений из геттеров:
- Для mutable-полей возвращайте глубокую копию объекта.
- Для коллекций возвращайте неизменяемое представление (например, через
Collections.unmodifiableList()) или копию.
Пример с коллекцией:
import java.util.*;
public final class ImmutableTeam {
private final String name;
private final List<String> members;
public ImmutableTeam(String name, List<String> members) {
this.name = name;
// 1. Защитная копия при инициализации
this.members = new ArrayList<>(members);
}
public String getName() { return name; }
public List<String> getMembers() {
// 2. Возвращаем неизменяемое представление внутренней коллекции
return Collections.unmodifiableList(members);
}
}
// Использование
List<String> originalList = new ArrayList<>(Arrays.asList("Alice", "Bob"));
ImmutableTeam team = new ImmutableTeam("Devs", originalList);
originalList.add("Charlie"); // Не влияет на team.members
team.getMembers().add("David"); // Выбросит UnsupportedOperationException
Соблюдение этих правил делает объект потокобезопасным по умолчанию и упрощает рассуждения о коде.