Статья

Неизменяемые (Immutable) типы в Java

В этом статье мы узнаем, что делает объект неизменяемым, как добиться неизменяемости в Java и какие преимущества это дает.

Что такое неизменяемый объект?

Неизменяемый объект – это объект, внутреннее состояние которого остается постоянным после того, как он был полностью создан.
Это означает, что открытый API неизменяемого объекта гарантирует нам, что он будет вести себя одинаково в течение всего срока службы.
Если мы посмотрим на класс String, мы увидим, что даже когда его API, кажется, предоставляет нам изменяемое поведение с помощью метода replace, исходная строка не меняется.
String str = "Immutable String";
  String newStr = str.replace("Immutable", "Mutable");
  System.out.println(str);
  System.out.println(newStr);
  System.out.println(str);

  // результат:

   Immutable String
   Mutable String
   Immutable String
API неизменяемого объекта предоставляет нам методы, доступные только для чтения, он никогда не должен включать методы, которые изменяют внутреннее состояние объекта.

Ключевое слово final в Java

Прежде чем пытаться добиться неизменяемости в Java, мы должны поговорить о ключевом слове final.
В Java переменные по умолчанию изменяемы, что означает, что мы можем изменить значение, которое они содержат.
Используя ключевое слово final при объявлении переменной, компилятор Java не позволит нам изменить значение этой переменной. Вместо этого он сообщит об ошибке во время компиляции:
final String name = "Java";
  name = "Python";
  // error: cannot assign a value to final variable name
Обратите внимание, что ключевое слово final только запрещает изменять ссылку, которую содержит переменная, но не защищает от изменения внутреннего состояния объекта, на который она ссылается, с помощью его общедоступного интерфейса:
final List<String> strings = new ArrayList<>();
  System.out.println(strings.size());
  strings.add("Java");
  System.out.println(strings.size());
  
  // результат:

  0
  1
В примере выше добавление элемента в список изменяет его размер, следовательно, он не является неизменяемым объектом.

Неизменяемость в Java

Теперь, когда мы знаем, как избежать изменений содержимого переменной, мы можем использовать ее для создания API неизменяемых объектов.
Создание API неизменяемого объекта требует от нас гарантии того, что его внутреннее состояние не изменится независимо от того, как мы используем его интерфейс.
Шагом в правильном направлении является использование final при объявлении его атрибутов:
class School {
     private final double number;
     private final Director director;
    
     // ...
  }
Обратите внимание, что Java гарантирует нам, что значение number не изменится, это относится ко всем переменным примитивного типа.

Однако в нашем примере нам гарантируется только то, что number не изменится, и мы должны полагаться на интерфейс Director, чтобы защитить себя от изменений.

В большинстве случаев нам нужны атрибуты объекта для хранения пользовательских значений, а местом инициализации внутреннего состояния неизменяемого объекта является его конструктор:
class School {
    // ...

    public School(double number, Director director) {
        this.number = number;
        this.director = director;
    }

    public Director getDirector() {
        return director;
    }

    public double getNumber() {
        return number;
    }
  }
Как мы уже говорили ранее, чтобы соответствовать требованиям неизменяемого API, наш класс School имеет методы, доступные только для чтения.
Используя reflection API, мы можем нарушать неизменяемость и изменять неизменяемые объекты. Однако reflection нарушает общедоступный интерфейс неизменяемого объекта, и обычно следует избегать этого.
Поскольку внутреннее состояние неизменяемого объекта остается постоянным во времени, мы можем безопасно разделить его между несколькими потоками. Можно сказать, что неизменяемые объекты не имеют побочных эффектов.

Заключение

Неизменяемые объекты не меняют свое внутреннее состояние во времени, они потокобезопасны и не имеют побочных эффектов. Из-за этих свойств неизменяемые объекты особенно полезны при работе с многопоточными средами.
java