JavaRush /Курсы /JAVA 25 SELF /Immutability — неизменяемость record-классов

Immutability — неизменяемость record-классов

JAVA 25 SELF
22 уровень , 1 лекция
Открыта

1. Record-классы и неизменяемость

Неизменяемость — это свойство объекта, при котором его состояние не может быть изменено после создания. Другими словами: если объект создан, вы не можете поменять его внутренние данные. Всё! Как в камне высечено.

Представьте билет на поезд. Как только он напечатан, вы не можете изменить дату или место отправления (ну, если не брать в расчёт хитрые махинации с фотошопом). Билет — неизменяемый объект. Если вы хотите другой билет — покупаете новый.

В программировании такие объекты называют immutable objects. Они защищают программу от случайных изменений, делают код более безопасным и предсказуемым.

Признаки неизменяемого объекта

  • Все поля объекта — final (их можно присвоить только один раз, обычно в конструкторе).
  • Нет сеттеров (методов, которые меняют значения полей).
  • Все методы, возвращающие внутренние данные, либо возвращают копии, либо сами данные тоже неизменяемы.

Record-классы в Java были придуманы именно для того, чтобы создавать неизменяемые структуры данных просто и без боли.

Почему record неизменяемый?

  • Все компоненты record — final.
    Когда вы объявляете record, компилятор автоматически делает все его поля private final. Это значит, что после создания объекта вы не сможете изменить его поля.
  • Нет сеттеров.
    В классе-record нельзя добавить метод setX(int x) — компилятор не позволит вам изменить значение поля после создания объекта.
  • Конструктор присваивает значения только один раз.
    Все значения задаются только в момент создания объекта.

Пример

public record Point(int x, int y) {}

Point p = new Point(5, 10);
// p.x = 7; // Ошибка компиляции: поле x имеет private-доступ и является final
// p.x(7);  // Ошибка: нет сеттера!
System.out.println(p.x()); // 5

Попытка изменить поле или вызвать несуществующий сеттер приводит к ошибке компиляции. Java строго следит за контрактом неизменяемости.

2. Преимущества неизменяемых объектов

Безопасность в многопоточности

В многопоточных программах (а таких сейчас большинство!) неизменяемые объекты — это как бронежилет. Если объект нельзя изменить, разные потоки могут его спокойно читать, не боясь, что кто-то в этот момент поменяет данные. Не нужно синхронизировать доступ и переживать из-за гонок данных.

Факт: многие классы в стандартной библиотеке Java, которые активно используются в многопоточных сценариях, либо неизменяемы, либо специально защищены от изменений.

Упрощение понимания кода

Если объект неизменяем, вы всегда уверены: передали его в другой метод или класс — и он не изменится «за вашей спиной». Это сильно облегчает чтение и отладку кода. Не нужно гадать, кто и где мог поменять поле, — никто не мог!

Удобно использовать как ключи в коллекциях

Неизменяемые объекты отлично подходят для использования в качестве ключей в коллекциях вроде HashMap или HashSet. Почему? Потому что их equals и hashCode зависят только от полей, которые не меняются. Значит, объект не «потеряется» в коллекции из-за того, что его состояние изменилось.

Меньше скрытых багов

Изменяемый объект легко испортить, случайно передав ссылку не туда. Неизменяемый же объект — как печатная книга: никто не может вырвать или переписать страницу.

3. Сравнение с обычными классами

Давайте сравним поведение обычного класса и record-класса. Для примера возьмём простую модель точки на плоскости.

Обычный класс (mutable)

public class PointClass {
    private int x;
    private int y;

    public PointClass(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { 
        return x; 
    }
    public int getY() { 
        return y; 
    }

    public void setX(int x) { 
        this.x = x; 
    }
    public void setY(int y) { 
        this.y = y; 
    }
}

Можно создать объект и потом сколько угодно менять его состояние:

PointClass p = new PointClass(1, 2);
p.setX(10); // p.x теперь 10

Record-класс (immutable)

public record Point(int x, int y) {}

Создаёте объект — и всё, он навсегда такой, каким был создан:

Point p = new Point(1, 2);
// p.x = 10;   // Ошибка! Нет доступа к полю
// p.x(10);    // Ошибка! Нет сеттера

Таблица: сравнение поведения

Обычный класс Record-класс
Поля любые только private final
Сеттеры можно добавить нельзя добавить
Изменяемость изменяемый (mutable) неизменяемый
Автогенерация нет да (equals, hashCode, toString)

4. Практика: как использовать неизменяемость record-классов

Давайте попробуем применить record-класс в небольшом примере. Пусть у нас есть банковское приложение, и мы хотим хранить информацию о транзакции:

public record Transaction(String fromAccount, String toAccount, double amount) {}

Создаём объект:

Transaction t = new Transaction("12345", "67890", 1500.0);
System.out.println(t);
// Transaction[fromAccount=12345, toAccount=67890, amount=1500.0]

Попробуем "перевести" деньги на другой счёт:

// t.toAccount = "11111"; // Ошибка! Поле final, доступа нет
// t.toAccount("11111");  // Ошибка! Нет сеттера

Если нам нужна другая транзакция — создаём новый объект:

Transaction t2 = new Transaction(t.fromAccount(), "11111", t.amount());

Важно: неизменяемость не значит «неудобно». Это просто другой стиль работы: если нужно новое состояние — создаём новый объект.

5. Особенности неизменяемости: что нужно помнить

Неизменяемость — не всегда абсолютна!

Record-класс гарантирует, что его поля не изменятся. Но если поле — это ссылка на изменяемый объект (например, массив или обычный класс), то содержимое этого объекта может быть изменено.

Пример с массивом

public record DataHolder(int[] data) {}

int[] arr = {1, 2, 3};
DataHolder holder = new DataHolder(arr);
arr[0] = 99;
System.out.println(holder.data()[0]); // 99! Массив изменился

Вывод: если хотите настоящую неизменяемость, используйте только неизменяемые типы (String, Integer, другие record’ы и т.д.) или делайте копии изменяемых объектов внутри канонического конструктора record-класса. Например:

int[] copy = Arrays.copyOf(data, data.length);

6. Как самому сделать обычный класс неизменяемым

Если вы хотите сделать обычный класс immutable (неизменяемым), придётся вручную:

  • Пометить все поля как private final,
  • Не добавлять сеттеров,
  • Все поля инициализировать только через конструктор,
  • Если поле — изменяемый объект, делать защитную копию (defensive copy).

Пример

public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { 
        return name; 
    }
    public int age() { 
        return age; 
    }
}

Согласитесь, с record-классом это делается проще и короче:

public record User(String name, int age) {}

7. Типичные ошибки при работе с неизменяемыми record-классами

Ошибка №1: попытка изменить поле после создания.
Новички часто пытаются написать p.x = 42; или p.x(42); для record-объекта. Но компилятор сразу скажет: «Так нельзя! Поле final, сеттера нет».

Ошибка №2: использование изменяемых объектов как компонентов record.
Если вы добавили в record поле типа List, Map, массив или другой изменяемый объект, то сам record не защитит вас от изменений содержимого этого объекта. Например, если у вас есть record User(List<String> hobbies), то кто-то может добавить или удалить элемент из списка, и это изменит состояние вашего record-объекта. Чтобы этого избежать, используйте неизменяемые коллекции (List.copyOf, Collections.unmodifiableList) или делайте копии коллекций внутри конструктора record.

Ошибка №3: неверное понимание неизменяемости.
Некоторые думают, что если объект — record, то он защищён от любых изменений. На самом деле, если поля — ссылки на изменяемые объекты, их содержимое можно поменять, и это приведёт к неожиданным багам.

1
Задача
JAVA 25 SELF, 22 уровень, 1 лекция
Недоступна
Запись в личную библиотеку 📖
Запись в личную библиотеку 📖
1
Задача
JAVA 25 SELF, 22 уровень, 1 лекция
Недоступна
Данные исторических личностей 🗿
Данные исторических личностей 🗿
1
Задача
JAVA 25 SELF, 22 уровень, 1 лекция
Недоступна
Перемещение объекта на игровой карте 🎮
Перемещение объекта на игровой карте 🎮
1
Задача
JAVA 25 SELF, 22 уровень, 1 лекция
Недоступна
Отслеживание показаний датчиков 📊
Отслеживание показаний датчиков 📊
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ioanna Polyak Уровень 43
6 ноября 2025
23🤣?