Ответ
Иммутабельный класс — это класс, экземпляры которого нельзя изменить после создания. Это ключевая концепция для написания потокобезопасного и предсказуемого кода.
Правила создания:
- Объявите класс как
final— запрещает наследование и переопределение методов. - Сделайте все поля
privateиfinal— гарантирует однократную инициализацию и скрывает состояние. - Не предоставляйте сеттеров (setter methods).
- Инициализируйте все поля через конструктор.
- Защитите от "утечки ссылок" (defensive copying):
- Для mutable-полей в конструкторе сохраняйте копию переданного объекта.
- Для геттеров, возвращающих mutable-поля, возвращайте копию или неизменяемое представление.
Пример:
import java.util.*;
public final class ImmutablePerson {
private final String name;
private final Date birthDate; // mutable класс!
private final List<String> hobbies;
public ImmutablePerson(String name, Date birthDate, List<String> hobbies) {
this.name = name;
// Защитная копия для mutable-объекта
this.birthDate = new Date(birthDate.getTime());
// Защитная копия для коллекции
this.hobbies = new ArrayList<>(hobbies);
}
public String getName() { return name; }
public Date getBirthDate() {
// Возвращаем копию, а не оригинальную ссылку
return new Date(birthDate.getTime());
}
public List<String> getHobbies() {
// Возвращаем неизменяемую обертку
return Collections.unmodifiableList(new ArrayList<>(hobbies));
}
}
Для удобного создания объектов со многими полями используйте паттерн Builder.
Ответ 18+ 🔞
Да ты посмотри на эту штуку — иммутабельный класс! Это ж как каменная баба, блядь, которую после установки уже ни сдвинуть, ни перекрасить. Создал — и спи спокойно, ни одна поехавшая нить исполнения тебе его не испоганит. Идеально для потоков, эти обоссанцы друг другу мозги не вынесут.
Вот как эту крепость строить, чтобы не развалилась:
- Класс в
finalи под замок, ёпта! Чтобы никакой умник-наследник не приперся со своими кривыми переопределениями и всё не раздолбал. - Поля —
private,finalи ни шага назад! Инициализировал один раз в конструкторе — и всё, приехали. Никаких задних дверей. - Сеттеры? На хуй сеттеры! Кто это придумал — менять что попало? Тут состояние раз и навсегда, как приговор.
- Всё загружаем через конструктор. Это наш главный и единственный пропускной пункт.
- А вот тут внимание, блядь, самый подлый подводный камень! Защита от утечки ссылок, или defensive copying. Если тебе в конструктор суют какой-нибудь изменяемый объект вроде
DateилиList— ты не ссылку на него сохраняй, а его копию, новенькую! И когда геттером это наружу выдаёшь — тоже копию или неизменяемую обёртку. А то придут эти пидарасы, возьмут твой «неизменяемый» объект, через геттер достанут список хобби и начнут там хуйню добавлять. И всё, пиздец твоей иммутабельности, все потоки в ахуе.
Смотри, как это выглядит в коде, на примере какого-нибудь «НесокрушимогоЧеловека»:
import java.util.*;
public final class ImmutablePerson {
private final String name;
private final Date birthDate; // А этот, сука, mutable! Коварная жопа!
private final List<String> hobbies;
public ImmutablePerson(String name, Date birthDate, List<String> hobbies) {
this.name = name;
// Не верь никому! Делаем копию переданной даты.
this.birthDate = new Date(birthDate.getTime());
// И список копируем, мало ли что там заслали.
this.hobbies = new ArrayList<>(hobbies);
}
public String getName() { return name; }
public Date getBirthDate() {
// Наружу тоже отдаём копию, а не оригинал. Пусть себе вертят.
return new Date(birthDate.getTime());
}
public List<String> getHobbies() {
// Возвращаем обёртку, которую нельзя изменить. Попробуй добавить хобби — получишь ебаный Exception.
return Collections.unmodifiableList(new ArrayList<>(hobbies));
}
}
А если полей, блядь, овердохуища, и конструктор получается на три экрана — не городи эту поебень. Используй паттерн Builder. Он тебе всё красиво соберёт по кирпичику, а в конце выдаст готовый, намертво зафиксированный объект. Удобно, как хуй с винтом.