← Назад к вопросам

Как создать Immutable объект?

2.0 Middle🔥 111 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как создать Immutable объект в Java

Immutable объект — это объект, состояние которого не может быть изменено после создания. Это один из важнейших паттернов для безопасности многопоточности и предсказуемости кода.

Правила создания Immutable объектов

  1. Сделай класс final — чтобы его нельзя было наследовать
  2. Все поля private final — чтобы нельзя было изменить
  3. Нет setter методов — никакого способа изменить состояние
  4. Инициализация только в конструкторе — только при создании
  5. Глубокое копирование — для mutable полей при возврате
  6. Глубокое копирование в конструкторе — для входящих 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 объектов

  1. Thread-safe по определению — нет нужды в синхронизации
  2. Можно кэшировать — состояние никогда не изменится
  3. Можно использовать как ключ в HashMap/HashSet
  4. Проще тестировать — нет побочных эффектов
  5. Проще понять код — состояние не может измениться
  6. Более безопасно — нет неожиданных изменений

Immutable объекты — это основа для написания надёжного, безопасного многопоточного кода в Java.