Ответ
Чтобы сделать объект иммутабельным (неизменяемым), необходимо следовать набору строгих правил при проектировании его класса. Это гарантирует, что состояние объекта останется постоянным после создания.
Пошаговый алгоритм:
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
Соблюдение этих правил делает объект потокобезопасным по умолчанию и упрощает рассуждения о коде.
Ответ 18+ 🔞
А, ну вот, смотри, опять эти ваши иммутабельные объекты, блядь. Ну, типа, чтобы твой класс был как гранитная глыба — создали и нихуя с ним не сделаешь. Чтоб не было этих сюрпризов, когда где-то в коде кто-то твою коллекцию нахуй переписал, а ты потом охуеешь. Так вот, правила, блядь, железные.
Как сделать, чтобы объект был как скала, ёпта:
- Класс —
final. Это чтоб какой-нибудь умник не наследовался от тебя и не наворотил в подклассе какую-нибудь хуйню, которая всё сломает. Запечатал нахуй, и всё. - Поля —
private final.Private— чтобы с улицы не лапали,final— чтобы один раз в конструкторе присвоил и забыл, как страшный сон. Менять? Не, не слышал. - Сеттеры? На хуй сеттеры! Вообще нихуя методов, которые меняют состояние. Только читать можно.
- Конструктор — чтоб не подсунули свинью.
- Примитивы да
String'и — похуй, присваивай. - А вот если тебе в конструктор передают какой-нибудь
Listили свой кастомный объект — тут, сука, внимание! Надо делать защитную копию (defensive copy). А то передадут тебе ссылку на свой список, ты её в своёfinal-поле запишешь, а они потом у себя этот список наизнанку вывернут — и твой "иммутабельный" объект, блядь, тоже поменяется! Пиздец и скандал. Копируй нахуй сразу.
- Примитивы да
- Геттеры — тоже с подвохом.
- Возвращаешь поле-коллекцию? Так нельзя, ёба! Вернёшь ссылку на внутренний список — и опять какой-нибудь пидор его изменит. Поэтому:
- Либо возвращай глубокую копию всего этого добра.
- Либо (для коллекций) — возвращай неизменяемое представление, обёртку, через которую нихуя не добавишь и не удалишь.
- Возвращаешь поле-коллекцию? Так нельзя, ёба! Вернёшь ссылку на внутренний список — и опять какой-нибудь пидор его изменит. Поэтому:
Смотри, как это в коде выглядит, на примере команды:
import java.util.*;
public final class ImmutableTeam {
private final String name;
private final List<String> members; // Список, блядь, mutable!
public ImmutableTeam(String name, List<String> members) {
this.name = name;
// ВАЖНО! Не this.members = members; // Это пиздец, ловушка!
// А делаем ЗАЩИТНУЮ КОПИЮ при создании:
this.members = new ArrayList<>(members);
}
public String getName() { return name; }
public List<String> getMembers() {
// ВАЖНО! Не return members; // Это опять пиздец, дадим доступ на изменение!
// А возвращаем НЕИЗМЕНЯЕМУЮ ОБЁРТКУ:
return Collections.unmodifiableList(members);
}
}
// Проверяем, что всё работает как надо
List<String> originalList = new ArrayList<>(Arrays.asList("Алиса", "Боб"));
ImmutableTeam team = new ImmutableTeam("Разработчики", originalList);
originalList.add("Чарли"); // Ха-ха, сука! На team.members это НЕ повлияло!
team.getMembers().add("Давид"); // А тут, блядь, вылетит UnsupportedOperationException! Нельзя изменять!
Вот так, ебушки-воробушки. Сделал по этим правилам — и спи спокойно. Объект твой теперь потокобезопасен по умолчанию (потому что менять нечего) и логику проще отслеживать — создался и застыл, как истукан.