Immutable объекты в Java. Назначение и использование.

Stonehenge

Что такое immutable объекты в Java

Это многофункциональные immutable (неизменяемые) объекты, которые можно применять в разных частях программы.

Например, это могут быть неизменяемые объекты в многопоточной среде для работы с данными. Также это могут быть сложные константы, которые помогают избежать излишнего использования памяти и перегрузки сборщика мусора (garbage collector). Отдельно стоит упомянуть иммутабельность при работе с коллекциями.

Основные параметры имутабельных классов (immutable classes)

Для того чтобы сказать, что класс является immutable (неизменяемым) он должен соотвествовать следующим критериям:

  • Не предоставляйте методы, которые модифицируют состояние объекта. Такие методы могут напрямую модифировать состояние объекта класса.
  • Убедитесь, что класс нельзя наследовать. Наследовав класс можно с легкостью получить доступ к его полям, где далее их можно легко модифицировать.
  • Сделайте все поля в классе final. Таким образом мы показывае наше намерение относительно того можно ли модифицировать поля в классе.
  • Сделайте все поля в классе private. Добавив этот атрибут к полю мы закроем доступ, где можно напрямую модифицировать поля класса.
  • Убедитесь, что класс не предоставляет эксклюзивный доступ к изменяемым объектам. Если например в классе существуют сложные объекты (коллекции, другие сложные классы и тп) нужно предосмотреть возоможность создания копии этого поля для того, чтобы не было возможности модифицировать его посредством ссылки.

Практическое применение immutable объектов

На практике, иммутабельность позволяет строить более стабильные программы и ее принципы зачастую применимы в фундаментальных частях софта.

Например, это могут быть сложные константы и строки, которые могут использоваться, как конфигурация программы или как базовые данные.

Сложные константы

В проекте может быть много статических констант для отображения разных состояний, статусов. Если взять, к примеру, то как работают под капотом базовые классы оберток простых типов можно увидеть что практически все они иммутабельны.

Например, класс Boolean и то как там устанавливаются состояния объекта:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

public static Boolean valueOf(boolean b) {
        return b ? TRUE : FALSE;
}

Также пример с BigInteger:

public static final BigInteger ZERO;
public static final BigInteger ONE;
public static final BigInteger TWO;
public static final BigInteger TEN;


private static final BigDecimal ZERO_THROUGH_TEN[] = {
    new BigDecimal(BigInteger.ZERO,       0,  0, 1),
    new BigDecimal(BigInteger.ONE,        1,  0, 1),
    new BigDecimal(BigInteger.TWO,        2,  0, 1),
    new BigDecimal(BigInteger.TEN,        10, 0, 2),
};

...

totalBig = totalBig.divide(BigInteger.TEN);

В многопоточности (java tread safety)

Java Concurrency

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

Для этой цели можно использовать ThreadLocal класс. Ниже приведен пример использования локальных переменных для каждого отдельного потока.

public class ThreadExample {
    
    public static final ThreadLocal<StateExampleHolder> statePerThreadExample = new ThreadLocal<StateExampleHolder>() {
        
        @Override
        protected StateExampleHolder initialValue() {
            return new StateExampleHolder("started");  
        }
    };

    public static StateExampleHolder getStatus() {
        return statePerThreadExample.get();
    }
}

В коллекциях

Как известно, зачастую, некоторые коллекции состоят из пары ключ, значение. Если рассмотреть более подробнее, например, коллекцию HashMap можно увидеть, что благодаря хешу ключа внутренний механизм коллекции определяет в какой бакет нужно положить объект.

Представьте, что ключ будет изменяемым. Допустим вы положили объект в коллекцию с определенным ключем, например, «one». Далее в процессе объект ключа поменял хеш код и внутренние состояние. Буквально значение стало «two».

Из примера выше, очевидно, что значение по ключу «one» мы уже потеряли и достать его из коллекции будет невозможно.

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

Строки (string) immutable

Strings

Строки пожалуй один из самых часто используемых классов. Ранее мы уже писали о String Pool, как хранятся строки в памыти, а также мы делали базовый обзор класса String в Java.

Как известно, сами строки не изменяемые (immutable). Каждый раз когда строка модифицируется, создается новый объект класса String. Поэтому для изменяемых строк сушествуют StringBuilder, StringBuffer.

А как обстоит дело со строковыми литерами? Если например меняется ссылка на строку, меняется ли и значение? Давайте рассмотрим пример:

String str1 = "Java is the best program language in the world";

String str2 = str1;
System.out.println(str2);

str1 = "No С++ the first";
System.out.println(str2);

Так как строки действительно являются не изменяемыми, результат будет очевиден:

Java is the best program language in the world

Java is the best program language in the world

Перечень часто используемых immutable классов в Java

Почти все классы обертки примитивных типов: Integer, String, Short, Boolean, BigInteger, BigDecimal и другие.

Дополнительно по теме, для тех кому лень читать:

Related posts

Leave a Comment