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

Какие знаешь характеристики Immutable объекта?

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

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

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

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

Характеристики Immutable объектов в Java

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

Основные характеристики Immutable объектов

1. Все поля должны быть final

Поля объекта объявляются с модификатором final, что гарантирует, что они не могут быть переассигнены:

public final class ImmutableUser {
    private final String name;
    private final int age;
    private final String email;
    
    public ImmutableUser(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getEmail() { return email; }
}

Почему это важно: Если поле не final, то Java компилятор не гарантирует, что оно не будет изменено через рефлексию или враждебный код.

2. Сам класс должен быть final

Класс объявляется final, чтобы его нельзя было наследовать и переопределять методы:

public final class ImmutableProduct {
    // ...
}

// Это приведёт к ошибке компиляции:
// public class MutableProduct extends ImmutableProduct { }

Почему это необходимо: Наследник мог бы переопределить getter и вернуть другое значение, нарушив иммутабельность.

3. Нет setter методов

Immutable объект не должен иметь методов, которые изменяют его состояние:

public final class ImmutableConfig {
    private final String host;
    private final int port;
    
    public ImmutableConfig(String host, int port) {
        this.host = host;
        this.port = port;
    }
    
    public String getHost() { return host; }
    public int getPort() { return port; }
    
    // НЕПРАВИЛЬНО: setter нарушает иммутабельность
    // public void setHost(String host) { this.host = host; }
}

4. Оборона против изменения через рефлексию

Для объектов со сложными полями нужно копировать состояние при инициализации и возврате:

public final class ImmutableCollection {
    private final List<String> items;
    
    // НЕПРАВИЛЬНО: передаём список напрямую
    // public ImmutableCollection(List<String> items) {
    //     this.items = items;  // Внешний код может изменить список!
    // }
    
    // ПРАВИЛЬНО: создаём копию
    public ImmutableCollection(List<String> items) {
        this.items = new ArrayList<>(items);
    }
    
    // НЕПРАВИЛЬНО: возвращаем список напрямую
    // public List<String> getItems() { return items; }
    
    // ПРАВИЛЬНО: возвращаем копию или unmodifiable view
    public List<String> getItems() {
        return Collections.unmodifiableList(new ArrayList<>(items));
    }
}

5. Обработка объектных полей

Если поле содержит объект (не примитив), необходимо защитить от внешних изменений:

public final class ImmutableAddress {
    private final String country;
    private final String city;
    
    public ImmutableAddress(String country, String city) {
        this.country = country;   // String — immutable, безопасно
        this.city = city;
    }
    
    public String getCountry() { return country; }
    public String getCity() { return city; }
}

public final class ImmutablePerson {
    private final String name;
    private final ImmutableAddress address;  // Immutable объект
    
    public ImmutablePerson(String name, ImmutableAddress address) {
        this.name = name;
        this.address = address;  // Безопасно, так как адрес immutable
    }
    
    public String getName() { return name; }
    public ImmutableAddress getAddress() { return address; }  // Возвращаем непосредственно
}

6. Правильная реализация с объектом

public final class ImmutableEmployee {
    private final String name;
    private final Date hireDate;  // Date — mutable!
    
    public ImmutableEmployee(String name, Date hireDate) {
        this.name = name;
        // Копируем Date, чтобы защиться от изменений снаружи
        this.hireDate = new Date(hireDate.getTime());
    }
    
    public String getName() { return name; }
    
    public Date getHireDate() {
        // Возвращаем копию, чтобы внешний код не изменил дату
        return new Date(hireDate.getTime());
    }
}

// Использование
Date originalDate = new Date();
ImmutableEmployee emp = new ImmutableEmployee("John", originalDate);
originalDate.setTime(System.currentTimeMillis());  // originalDate изменится, но объект будет не затронут
Date retrievedDate = emp.getHireDate();
retrievedDate.setTime(System.currentTimeMillis());  // retrievedDate изменится, но объект будет не затронут

Примеры Immutable классов в Java

Встроенные immutable классы:

  • String — классический пример
  • Integer, Long, Double, другие обёртки примитивов
  • BigDecimal, BigInteger
  • LocalDate, LocalTime, LocalDateTime (java.time)
  • Collections.unmodifiableList(), Collections.unmodifiableSet()
String str = "Hello";
// Все операции возвращают новые String объекты
String upper = str.toUpperCase();  // Возвращает новый объект
String substring = str.substring(0, 2);  // Возвращает новый объект

Преимущества Immutable объектов

1. Потокобезопасность

// Несколько потоков могут безопасно использовать один объект
public final class ImmutableCache {
    private final Map<String, String> cache;
    
    public ImmutableCache(Map<String, String> initialCache) {
        this.cache = Collections.unmodifiableMap(new HashMap<>(initialCache));
    }
    
    public String get(String key) {
        return cache.get(key);  // Потокобезопасно, синхронизация не нужна
    }
}

2. Простота использования в Collections

Set<ImmutableUser> users = new HashSet<>();
users.add(new ImmutableUser("John", 30, "john@example.com"));
// Безопасно: хеш-код не изменится

3. Удобство для функционального программирования

List<ImmutableUser> users = Arrays.asList(
    new ImmutableUser("Alice", 25, "alice@example.com"),
    new ImmutableUser("Bob", 30, "bob@example.com")
);

users.stream()
     .filter(u -> u.getAge() > 26)
     .forEach(System.out::println);

4. Кэшируемость и реиспользование

public final class Color {
    private static final Color RED = new Color(255, 0, 0);
    private static final Color GREEN = new Color(0, 255, 0);
    private static final Color BLUE = new Color(0, 0, 255);
    
    private final int r, g, b;
    
    private Color(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }
    
    public static Color getInstance(int r, int g, int b) {
        if (r == 255 && g == 0 && b == 0) return RED;
        if (r == 0 && g == 255 && b == 0) return GREEN;
        if (r == 0 && g == 0 && b == 255) return BLUE;
        return new Color(r, g, b);
    }
}

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

Builder для удобного создания

public final class ImmutableUser {
    private final String name;
    private final String email;
    private final int age;
    
    private ImmutableUser(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.age = builder.age;
    }
    
    public static class Builder {
        private String name;
        private String email;
        private int age;
        
        public Builder name(String name) { this.name = name; return this; }
        public Builder email(String email) { this.email = email; return this; }
        public Builder age(int age) { this.age = age; return this; }
        
        public ImmutableUser build() {
            return new ImmutableUser(this);
        }
    }
}

// Использование
ImmutableUser user = new ImmutableUser.Builder()
    .name("John")
    .email("john@example.com")
    .age(30)
    .build();

Java 14+: Record классы

Modern Java предоставляет record для удобного создания immutable классов:

public record User(String name, String email, int age) {
    // Автоматически получаем:
    // - private final поля
    // - конструктор
    // - getters (name(), email(), age())
    // - equals(), hashCode(), toString()
}

// Использование
User user = new User("John", "john@example.com", 30);
String name = user.name();  // getter

Заключение

Immutable объекты — это фундаментальный паттерн для написания безопасного, понятного и масштабируемого кода. Основные правила: final на поля и класс, отсутствие setters, защита от модификаций сложных типов, возврат копий или unmodifiable views. Java 14+ предоставляет record для простого создания таких объектов.