Что такое Record и как он помогает создавать иммутабельные классы?
Одна строка вместо 20+. Автоматически:
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. Как правильно работать с коллекциями в иммутабельных классах]]