Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как создать Immutable объект в Java
Immutable объект — это объект, состояние которого не может быть изменено после создания. Это один из важнейших паттернов для безопасности многопоточности и предсказуемости кода.
Правила создания Immutable объектов
- Сделай класс final — чтобы его нельзя было наследовать
- Все поля private final — чтобы нельзя было изменить
- Нет setter методов — никакого способа изменить состояние
- Инициализация только в конструкторе — только при создании
- Глубокое копирование — для mutable полей при возврате
- Глубокое копирование в конструкторе — для входящих mutable аргументов
Простой пример
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name; // String immutable, можно просто вернуть
}
public int getAge() {
return age; // примитив, автоматически immutable
}
}
// Использование
Person person = new Person("John", 30);
String name = person.getName();
// Больше нет способа изменить состояние person
Сложный пример с mutable полями
public final class User {
private final String username;
private final List<String> roles; // MUTABLE!
private final Address address; // MUTABLE объект!
// НЕПРАВИЛЬНО:
public User(String username, List<String> roles, Address address) {
this.username = username;
this.roles = roles; // Ошибка: та же ссылка!
this.address = address; // Ошибка: та же ссылка!
}
public List<String> getRoles() {
return roles; // Ошибка: можно изменить внешне!
}
}
// Проблема:
List<String> rolesList = new ArrayList<>(Arrays.asList("USER"));
User user = new User("john", rolesList, addr);
rolesList.add("ADMIN"); // Внешне изменили состояние user!
System.out.println(user.getRoles()); // [USER, ADMIN] — неправильно!
Правильная реализация
public final class User {
private final String username;
private final List<String> roles; // Immutable список
private final Address address; // Immutable копия
// ПРАВИЛЬНО: глубокое копирование при создании
public User(String username, List<String> roles, Address address) {
this.username = username;
// Копируем список и делаем его неизменяемым
this.roles = Collections.unmodifiableList(
new ArrayList<>(roles)
);
// Копируем Address (предполагаем, что Address тоже immutable)
this.address = new Address(address);
}
public String getUsername() {
return username; // String immutable
}
public List<String> getRoles() {
// Уже unmodifiable, но можно дополнительно защитить
return new ArrayList<>(roles); // Копия для полной защиты
}
public Address getAddress() {
// Копируем для защиты (или возвращаем, если Address immutable)
return new Address(address);
}
}
public final class Address {
private final String street;
private final String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
// Copy constructor
public Address(Address other) {
this.street = other.street;
this.city = other.city;
}
public String getStreet() { return street; }
public String getCity() { return city; }
}
// Использование
List<String> rolesList = new ArrayList<>(Arrays.asList("USER"));
User user = new User("john", rolesList, new Address("Main St", "NYC"));
rolesList.add("ADMIN"); // Внешне изменили список
System.out.println(user.getRoles()); // [USER] — защищено!
Использование Collections.unmodifiableX
// Unmodifiable List
List<String> list = Collections.unmodifiableList(
new ArrayList<>(Arrays.asList("a", "b", "c"))
);
// list.add("d"); // UnsupportedOperationException
// Unmodifiable Map
Map<String, String> map = Collections.unmodifiableMap(
new HashMap<>(Map.of("key", "value"))
);
// map.put("key2", "value2"); // UnsupportedOperationException
// Unmodifiable Set
Set<String> set = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("a", "b"))
);
// set.add("c"); // UnsupportedOperationException
Java 9+: Immutable Collections
// Более удобно с Java 9+
public final class User {
private final List<String> roles;
public User(String username, List<String> roles) {
this.username = username;
// Неизменяемый список в одну строку
this.roles = List.copyOf(roles);
}
public List<String> getRoles() {
return roles; // Уже unmodifiable
}
}
// Аналогично для других типов
Set<String> set = Set.copyOf(originalSet);
Map<String, String> map = Map.copyOf(originalMap);
Record класс (Java 14+)
Record — это специальный синтаксис для immutable объектов:
public record Person(
String name,
int age
) {
// Автоматически:
// - final класс
// - private final поля
// - getters (name(), age())
// - toString(), equals(), hashCode()
}
// Использование
Person person = new Person("John", 30);
String name = person.name();
// Но для mutable полей нужно быть осторожным
public record UserRecord(
String username,
List<String> roles
) {
// Валидация и защита от mutable полей
public UserRecord {
roles = List.copyOf(roles); // Конструктор Record
}
public List<String> getRoles() {
return roles; // Уже защищено в конструкторе
}
}
Builder Pattern для immutable объектов
public final class User {
private final String username;
private final String email;
private final int age;
private final List<String> roles;
// Private конструктор
private User(Builder builder) {
this.username = builder.username;
this.email = builder.email;
this.age = builder.age;
this.roles = List.copyOf(builder.roles);
}
// Static builder класс
public static class Builder {
private String username;
private String email;
private int age;
private List<String> roles = new ArrayList<>();
public Builder username(String username) {
this.username = username;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder addRole(String role) {
this.roles.add(role);
return this;
}
public User build() {
if (username == null) {
throw new IllegalStateException("Username required");
}
return new User(this);
}
}
// Getters
public String getUsername() { return username; }
public String getEmail() { return email; }
public int getAge() { return age; }
public List<String> getRoles() { return new ArrayList<>(roles); }
}
// Использование
User user = new User.Builder()
.username("john")
.email("john@example.com")
.age(30)
.addRole("USER")
.addRole("ADMIN")
.build();
Чеклист для Immutable объекта
✅ Класс помечен как final ✅ Все поля private final ✅ Нет setter методов ✅ Нет getter методов, возвращающих mutable объекты ✅ Глубокое копирование входящих mutable аргументов в конструкторе ✅ Глубокое копирование при возврате mutable объектов из getter ✅ Валидация данных в конструкторе ✅ Переопределены equals() и hashCode() ✅ Все мutable поля инициализированы в конструкторе
Преимущества Immutable объектов
- Thread-safe по определению — нет нужды в синхронизации
- Можно кэшировать — состояние никогда не изменится
- Можно использовать как ключ в HashMap/HashSet
- Проще тестировать — нет побочных эффектов
- Проще понять код — состояние не может измениться
- Более безопасно — нет неожиданных изменений
Immutable объекты — это основа для написания надёжного, безопасного многопоточного кода в Java.