Что такое иммутабельный (immutable) объект и каковы его преимущества?

«Что такое иммутабельный (immutable) объект и каковы его преимущества?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Ключевые характеристики и преимущества:

  1. Потокобезопасность (Thread-safe): Поскольку состояние объекта неизменно, его можно свободно использовать в многопоточных средах без синхронизации. Несколько потоков могут читать данные одновременно без риска конфликтов.
  2. Простота и предсказуемость: Упрощает рассуждения о коде. Гарантируется, что объект, переданный в метод, не будет изменен где-то еще.
  3. Безопасное кэширование и повторное использование: Объекты можно безопасно кэшировать и использовать как константы. Классический пример — String и пул строк в Java.
  4. Надежные ключи для коллекций: Идеально подходят для использования в качестве ключей в HashMap или HashSet, так как их hashCode() не изменяется.

Правила создания иммутабельного класса в Java:

  • Объявите класс как final, чтобы от него нельзя было наследоваться.
  • Объявите все поля как private и final.
  • Не предоставляйте сеттеров (setter methods).
  • Если класс содержит поля-ссылки на изменяемые объекты, в конструкторе и геттерах создавайте защитные копии (defensive copies).

Пример:

import java.util.*;

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;
        // Защитная копия при инициализации
        this.hobbies = new ArrayList<>(hobbies);
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    // Защитная копия при возврате значения
    public List<String> getHobbies() {
        return new ArrayList<>(hobbies);
    }
}
// Использование
ImmutablePerson person = new ImmutablePerson("Alice", 30, Arrays.asList("Reading", "Cycling"));
// person.getHobbies().add("Hacking"); // Не изменит оригинальный список hobbies