JavaRush /Курсы /JAVA 25 SELF /Отличия record и class, ограничения record

Отличия record и class, ограничения record

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

1. Сравнение record и class: в чём главные отличия?

В Java у нас есть два основных способа описывать свои типы данных: через обычные классы (class) и через record-классы (record). На первый взгляд, оба варианта позволяют хранить и обрабатывать данные. Но если копнуть чуть глубже, различий больше, чем может показаться!

Таблица отличий: class vs record

Характеристика Обычный класс (class) Record-класс (record)
Изменяемость Любая: можно делать поля final или нет Неизменяемый: все поля final
Наследование Можно наследовать (extends), не final по умолчанию Всегда final, не может быть суперклассом
Поля Любые: статические, нестатические, final или не-final, любые типы Только компоненты record (private final), плюс статические поля
Геттеры/сеттеры Сами пишем (или генерируем Lombok'ом) Автоматически создаются геттеры (имя поля как имя метода), сеттеров нет
equals/hashCode/toString Обычно пишем вручную/генерируем (equals, hashCode, toString) Генерируются автоматически по всем компонентам
Конструкторы Любые, сколько угодно Один основной (по всем компонентам), можно добавить компактный конструктор
Интерфейсы Можно реализовывать Можно реализовывать
Дополнительные методы Любые Можно добавлять, но только методы (не поля)
Использование в коллекциях Можно, но нужно правильно реализовать equals/hashCode Идеально подходит для ключей/значений, всё уже реализовано

Пример для наглядности

Обычный класс:


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

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

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

    @Override
    public boolean equals(Object o) { /* ... */ }
    @Override
    public int hashCode() { /* ... */ }
    @Override
    public String toString() { /* ... */ }
}

Record-класс:


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

Всё! Одна строчка кода — и мы получаем то же самое (и даже лучше). И никаких рисков забыть реализовать что-то важное.

2. Ограничения record-классов

Record-классы — это не просто «короткий синтаксис», а отдельная концепция, у которой есть свои строгие правила. Давайте рассмотрим их подробнее.

Record всегда final

Record-класс по определению всегда final. Это значит, вы не можете создать подкласс от record:


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

// public class ColoredPoint extends Point { } // Ошибка компиляции!

Если вам нужно расширять поведение, используйте обычные классы или композицию (вложите record в класс).

Record не может быть суперклассом

Record-класс не может быть родителем для других классов, он всегда final. Это логично: если бы это было возможно, кто-то мог бы добавить изменяемое поле — и вся концепция «неизменяемых данных» рухнула бы.

Только final-поля (компоненты)

Все компоненты record объявляются в заголовке и по умолчанию private final. Вы не можете добавить нестатические поля в тело record:


public record User(String login, String email) {
    // int counter; // Ошибка! Нельзя добавлять нестатические поля
    static int totalUsers = 0; // Можно, это статическое поле
}

Нет сеттеров

Record-класс не может иметь сеттеров для компонентов. Любая попытка добавить метод вроде setX(int x) будет бессмысленной: вы не сможете изменить значение поля после создания объекта.


public record Point(int x, int y) {
    // public void setX(int x) { this.x = x; } // Ошибка: нельзя изменить final-поле
}

Нет пустого конструктора

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


Point p = new Point(1, 2);  // ОК
// Point p = new Point();   // Ошибка: нет конструктора без параметров

Нет нестатических инициализаторов

Record-класс не может содержать нестатические инициализаторы (те, что пишутся в фигурных скобках вне методов):


public record User(String login) {
    // { /* ... */ } // Ошибка: нестатические инициализаторы запрещены
}

Ограничения по наследованию

Record-класс не может явно наследовать другой класс (кроме java.lang.Record, который скрыт от нас как базовый класс для всех record'ов). Но реализовывать интерфейсы — пожалуйста!


public interface Printable {
    void print();
}

public record Book(String title) implements Printable {
    @Override
    public void print() {
        System.out.println("Печатаем книгу: " + title);
    }
}

Не подходит для сложной бизнес-логики

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

3. Когда стоит использовать record-классы?

  • DTO (Data Transfer Object): для передачи неизменяемых данных между слоями приложения, сервисами, микросервисами или REST-контроллерами (например, в JSON-ответах).
  • Value Object: объекты, которые определяются только своими значениями.
  • Ключи и значения в коллекциях: когда важна корректная реализация equals и hashCode (например, для использования в HashMap или Set).
  • Результаты вычислений: когда нужно вернуть из метода сразу несколько значений (например, record Pair<T, U>(T first, U second)).

Пример: DTO для REST-контроллера


public record UserDto(String login, String email) { }

Теперь можно смело возвращать объект этого типа из контроллера, не боясь, что кто-то изменит его поля.

Пример: Ключ для HashMap


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

Map<Point, String> pointNames = new HashMap<>();
pointNames.put(new Point(1, 2), "A");
pointNames.put(new Point(3, 4), "B");

// Всё работает корректно: equals и hashCode уже реализованы!

4. Когда record-классы использовать НЕ стоит

  • Изменяемое состояние: если хотя бы одно поле должно меняться после создания объекта.
  • Сложная логика: если у объекта сложное поведение, много методов, вложенные объекты с изменяемым состоянием.
  • Наследование: если требуется иерархия классов, абстрактные базовые классы, переопределение методов.
  • Сущности бизнес-логики: например, объекты, которые живут в базе данных и имеют уникальный идентификатор.

Пример: когда нужен обычный класс


public class Account {
    private String id;
    private int balance;

    public Account(String id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void deposit(int amount) { balance += amount; }
    public void withdraw(int amount) { balance -= amount; }
    // getters, setters, equals, hashCode, toString...
}

Здесь явно видно, что состояние объекта меняется — record не подходит.

5. Практические примеры: выбираем между record и class

Пример 1: record — идеальный выбор


public record Rectangle(int width, int height) {
    public int area() {
        return width * height;
    }
}
  • Прямоугольник определяется только шириной и высотой.
  • Нет необходимости менять эти значения после создания.
  • Можно добавить полезный метод area().
  • Всё остальное Java сделает за вас.

Пример 2: class — лучший вариант


public class MutableRectangle {
    private int width;
    private int height;

    public MutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void setWidth(int width) { this.width = width; }
    public void setHeight(int height) { this.height = height; }

    public int area() { return width * height; }
}

Нужно изменять размеры прямоугольника после создания? Используйте обычный класс.

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

Ошибка №1: попытка добавить нестатическое поле.
Record-класс не позволяет объявлять нестатические поля вне списка компонентов. Если попытаться — компилятор выдаст ошибку. Например:


public record City(String name) {
    // int population; // Ошибка!
}

Ошибка №2: желание добавить сеттер.
Record не поддерживает сеттеры для компонентов. Любая попытка изменить значение поля после создания объекта — ошибка компиляции.

Ошибка №3: попытка наследовать record или от record.
Record всегда final. Нельзя наследовать от record, и record не может наследовать другой класс (кроме скрытого java.lang.Record).

Ошибка №4: использование record для mutable-объектов.
Если вы планируете менять состояние объекта после создания — record не для вас! Используйте обычный класс.

Ошибка №5: забыли про ограничения конструктора.
У record-класса обязательно должен быть конструктор, принимающий значения для всех компонентов. Конструктора без параметров нет!

1
Задача
JAVA 25 SELF, 22 уровень, 4 лекция
Недоступна
Каталогизация домашней библиотеки 📚
Каталогизация домашней библиотеки 📚
1
Задача
JAVA 25 SELF, 22 уровень, 4 лекция
Недоступна
Проверка совпадения координат на карте 🗺️
Проверка совпадения координат на карте 🗺️
1
Задача
JAVA 25 SELF, 22 уровень, 4 лекция
Недоступна
Печать содержимого документа 📄
Печать содержимого документа 📄
1
Задача
JAVA 25 SELF, 22 уровень, 4 лекция
Недоступна
Сравнение изменяемых и неизменяемых профилей пользователей 🧑‍🤝‍🧑
Сравнение изменяемых и неизменяемых профилей пользователей 🧑‍🤝‍🧑
1
Опрос
Record-классы, 22 уровень, 4 лекция
Недоступен
Record-классы
Record-классы
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Юрий Уровень 26
28 ноября 2025
зачем из лекции в лекцию писать одну и туже информацию?
qaruller Уровень 28
16 января 2026
Так лучше запоминается, без шуток