Вопрос 20 · Раздел 13

Что такое Record и как он помогает создавать иммутабельные классы?

Одна строка вместо 20+. Автоматически:

Версии по языкам: English Russian Ukrainian

Junior Level

Record — это специальный тип класса в Java (появился в Java 14, стабилен с Java 16), который автоматически создаёт иммутабельный класс для хранения данных.

До Record

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 getX() { return x; }
    public int getY() { return y; }

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

С Record

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

Одна строка вместо 20+. Автоматически:

  • Класс final
  • Поля private final
  • Конструктор
  • Геттеры (x(), y() — не getX())
  • equals(), hashCode(), toString()

Middle Level

Что делает компилятор за вас

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

Генерируется:

public final class User extends java.lang.Record {
    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; }

    // equals, hashCode, toString — по всем полям
}

Поверхностная иммутабельность

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

public record Order(Long id, List<String> items) {}

Order order = new Order(1L, new ArrayList<>(List.of("A")));
order.items().add("B"); // МОЖНО! items — тот же список

Решение — компактный конструктор

public record Order(Long id, List<String> items) {
    public Order {
        items = List.copyOf(items); // защитная копия
    }
}

Когда использовать

  • DTO — передача данных между слоями
  • Ключи в Map — стабильный hashCode
  • Локальные структуры — временное хранение кортежей

Senior Level

Ограничения

  • Не может наследоваться от других классов (уже наследует java.lang.Record)
  • Не может быть базовым классом (всегда final)
  • Не может иметь instance поля кроме объявленных в заголовке
  • Не может быть abstract

Record vs Lombok @Value vs обычный final class

  Record Lombok @Value Final class
Boilerplate Минимум Минимум Вручную
Наследование Можно Можно
Extra-поля Можно Можно
Зависимости JDK Lombok Нет

Компактный конструктор и валидация

public record User(String email, int age) {
    public User {
        Objects.requireNonNull(email, "Email required");
        if (age < 0) throw new IllegalArgumentException("Age must be positive");
    }
}

Кастомные методы

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

Резюме для Senior

  • record — специальная языковая конструкция для неизменяемых классов-значений. Это не просто sugar — record имеет собственные ограничения (нельзя наследовать, нельзя иметь instance-поля кроме declared).
  • Радикально снижает объём кода и вероятность ошибки в equals/hashCode
  • Ручное копирование коллекций всё ещё необходимо
  • Используйте для DTO, ключей Map, временных структур
  • Не может участвовать в иерархии наследования

🎯 Шпаргалка для интервью

Обязательно знать:

  • Record (Java 14+, стабилен Java 16) — специальная языковая конструкция для неизменяемых классов-значений
  • Автоматически: final класс, private final поля, конструктор, геттеры (name() не getName()), equals/hashCode/toString
  • Поверхностная иммутабельность: ссылки неизменны, но мутабельные коллекции можно менять
  • Компактный конструктор: public Order { items = List.copyOf(items); } — для защитного копирования
  • Ограничения: нет наследования, нет extra instance-полей, нет abstract
  • Record vs Lombok @Value: Record — JDK, без зависимостей; Lombok — больше гибкости

Частые уточняющие вопросы:

  • Record делает deep copy коллекций? — Нет, только shallow; нужно вручную в компактном конструкторе
  • Можно ли добавить поле в Record? — Нет, только объявленные в заголовке
  • Record можно наследовать? — Нет, всегда final; не может быть базовым классом
  • Геттеры в Record — getX()? — Нет, просто x() — без префикса get

Красные флаги (НЕ говорить):

  • «Record полностью иммутабелен» — коллекции нужно копировать вручную
  • «Record можно расширить» — всегда final, нет наследования
  • «Record заменяет Lombok» — Lombok @Builder, @Singular дают больше функциональности
  • «Record — это просто sugar» — record имеет собственные ограничения и семантику

Связанные темы:

  • [[1. Что такое иммутабельный (неизменяемый) объект]]
  • [[3. Как создать иммутабельный класс в Java]]
  • [[26. Как реализовать Builder pattern для иммутабельного класса]]
  • [[27. Можно ли использовать иммутабельные объекты как ключи в HashMap]]
  • [[29. Как правильно работать с коллекциями в иммутабельных классах]]