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

Как сделать класс immutable (неизменяемым) в Java?

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

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

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

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

Как сделать класс immutable (неизменяемым) в Java?

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

Основные правила создания immutable класса

Для создания полностью неизменяемого класса необходимо следовать нескольким строгим правилам:

1. Объявить класс как final Это предотвращает наследование, которое могло бы нарушить immutability через переопределение методов.

2. Сделать все поля private и final Убедиться, что поля не могут быть переназначены и не доступны извне.

3. Не предоставлять setter методы Любые методы, которые модифицируют объект, должны быть полностью исключены.

4. Если поля содержат mutable объекты, выполнить deep copy Риск в том, что внешний код может получить ссылку на изменяемый объект и модифицировать его, косвенно изменяя состояние immutable объекта.

Пример реализации

public final class ImmutablePerson {
    private final String name;
    private final int age;
    private final List<String> hobbies;
    
    public ImmutablePerson(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        // Deep copy для защиты от внешних модификаций
        this.hobbies = new ArrayList<>(hobbies);
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    // Возвращаем неизменяемую копию
    public List<String> getHobbies() {
        return Collections.unmodifiableList(new ArrayList<>(hobbies));
    }
}

Защита от модификации mutable полей

Рассмотрим случай с более сложной структурой данных:

public final class ImmutableUser {
    private final String email;
    private final Date createdAt;
    private final List<String> permissions;
    
    public ImmutableUser(String email, Date createdAt, List<String> permissions) {
        this.email = email;
        // Deep copy объекта Date
        this.createdAt = new Date(createdAt.getTime());
        // Deep copy List
        this.permissions = new ArrayList<>(permissions);
    }
    
    public String getEmail() {
        return email;
    }
    
    public Date getCreatedAt() {
        // Возвращаем копию, а не оригинал
        return new Date(createdAt.getTime());
    }
    
    public List<String> getPermissions() {
        return Collections.unmodifiableList(new ArrayList<>(permissions));
    }
}

Использование Builder pattern

Для удобства создания immutable объектов с множеством параметров используется Builder pattern:

public final class ImmutableProduct {
    private final String id;
    private final String name;
    private final double price;
    private final String description;
    
    private ImmutableProduct(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.price = builder.price;
        this.description = builder.description;
    }
    
    public static class Builder {
        private String id;
        private String name;
        private double price;
        private String description;
        
        public Builder id(String id) {
            this.id = id;
            return this;
        }
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public Builder price(double price) {
            this.price = price;
            return this;
        }
        
        public Builder description(String description) {
            this.description = description;
            return this;
        }
        
        public ImmutableProduct build() {
            return new ImmutableProduct(this);
        }
    }
    
    public String getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }
    public String getDescription() { return description; }
}

Использование записей (Records) в Java 14+

С введением Records в Java 14, создание immutable классов стало намного проще:

public record ImmutableCar(
    String brand,
    String model,
    int year
) {}

Records автоматически создают private final поля, конструктор, getters и equals/hashCode методы. Это идеальное решение для immutable data-классов.

Преимущества immutable классов

  • Потокобезопасность: Можно безопасно использовать в многопоточной среде без синхронизации
  • Простота отладки: Состояние объекта не меняется, что упрощает отслеживание багов
  • Кэширование: Можно кэшировать результаты, так как они не изменяются
  • Производительность: JVM может применять различные оптимизации
  • Безопасность: Идеально подходит для использования в качестве ключей в HashMap

Когда использовать immutability

  • Объекты передаются между потоками
  • Используются как ключи в collections
  • Нужна простота и надежность кода
  • Требуется кэширование результатов
  • Работа с многопоточностью без явной синхронизации