Gymterview
junior

Records — что это и зачем?

Record — компактный способ объявления классов-носителей неизменяемых данных (data carriers). Компилятор автоматически генерирует конструктор, accessor-методы, equals(), hashCode() и toString().

Аналогия из жизни: record — как бланк паспорта: поля фиксированы (имя, дата рождения, номер), нельзя добавить новые, нельзя изменить заполненные. Вы просто указываете, какие данные нужны, а формат генерируется автоматически.

Пример: Record vs обычный класс
// Record — одна строка вместо ~50 строк boilerplate
public record Point(int x, int y) {}

// Эквивалентный обычный класс:
public final class Point {
    private final int x;
    private final int y;

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

    public int x() { return x; }     // не getX()!
    public int y() { return y; }

    @Override
    public boolean equals(Object o) { ... }

    @Override
    public int hashCode() { ... }

    @Override
    public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}

Использование

Пример
var p = new Point(10, 20);
int x = p.x();           // accessor (не getX!)
System.out.println(p);   // Point[x=10, y=20]

// Compact constructor — валидация
public record Email(String value) {
    public Email {   // compact canonical constructor
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("Invalid email: " + value);
        }
        value = value.toLowerCase().strip();
    }
}

// Record с дополнительными методами
public record Range(int from, int to) {
    public Range {
        if (from > to) throw new IllegalArgumentException();
    }
    public int length() { return to - from; }
    public boolean contains(int value) { return value >= from && value <= to; }
}

Ограничения Records

Ограничение Причина
Нельзя наследоваться от другого класса Неявно наследует java.lang.Record
Нельзя объявлять instance-поля Только компоненты записи
Все поля final и private Неизменяемость по контракту
Record implicitly final Нельзя наследоваться от record
Нельзя как JPA Entity Entity требует no-arg constructor и мутабельность

Частые ошибки

  • Мутабельные поля в Record — record Data(List<String> items) — список мутабельный; делайте defensive copy: this.items = List.copyOf(items)
  • Record как JPA Entity — нельзя; Record можно как DTO или @Embeddable
  • Ожидать getX() вместо x() — Jackson и другие библиотеки поддерживают record accessors, но legacy-код может ожидать JavaBean-стиль

Как используется в 2026

  • Records — стандарт для DTO, Value Objects, API responses/requests
  • Широкая поддержка: Jackson, Spring, Hibernate (Embeddable), MapStruct
  • В комбинации с Sealed Interfaces — алгебраические типы данных
  • Record Patterns (Java 21) — деконструкция records в switch/instanceof

На собеседовании: не забудьте упомянуть compact constructor (валидация без явного присвоения полей), accessor без get-префикса (x() вместо getX()), и невозможность использования как JPA Entity. Частая ошибка — не знать про defensive copy для мутабельных компонентов.