Record в Java: руководство по лучшей функции, которую вы не используете
Устали писать горы шаблонного кода для простых классов данных? equals, hashCode, toString, геттеры... Знакомо? Тогда встречайте Records (записи) — возможно, лучшая фича Java, которая сэкономит вам кучу времени и нервов.
❌ Проблема: Класс "вручную"
Допустим, нам нужен класс Employee (Сотрудник). Вот как он выглядит по-старинке:
public final class Employee {
private final String name;
private final int employeeNumber;
public Employee(String name, int employeeNumber) {
this.name = name;
this.employeeNumber = employeeNumber;
}
public String getName() { return name; }
public int getEmployeeNumber() { return employeeNumber; }
@Override
public String toString() {
return "Employee[name=" + name + ", employeeNumber=" + employeeNumber + "]";
}
@Override
public boolean equals(Object o) {
// ... Длинная реализация с проверками ...
}
@Override
public int hashCode() {
// ... Ещё одна реализация ...
}
}Итого: ~50 строк кода для хранения двух полей! Большую часть этого кода можно сгенерировать, но его всё равно нужно поддерживать и читать.
✅ Решение: Record
А теперь посмотрите на магию:
public record Employee(String name, int employeeNumber) {}Всё! Серьёзно, это полный аналог класса выше. Всего одна строка!
Что же такое Record?
Запись — это специальный вид класса, главная цель которого — быть неизменяемым (immutable) контейнером для данных.
Что компилятор генерирует за вас автоматически:
Приватные и финальные поля для каждого компонента (
name,employeeNumber).Канонический конструктор —
Employee(String name, int employeeNumber).Геттеры (но без префикса
get!) —name()иemployeeNumber().Методы
toString(),equals(Object o)иhashCode().
Пример использования:
// Создание записи
Employee john = new Employee("John Doe", 12345);
// Использование геттеров (без "get"!)
System.out.println(john.name()); // John Doe
System.out.println(john.employeeNumber()); // 12345
// Автоматический toString()
System.out.println(john); // Employee[name=John Doe, employeeNumber=12345]
// Работает equals и hashCode
Employee johnClone = new Employee("John Doe", 12345);
System.out.println(john.equals(johnClone)); // trueСуперсила: Кастомизация записей
Записи не совсем "закрытые коробки". Вы можете добавлять свою логику!
1. Компактный конструктор
Идеален для валидации. Параметры и присваивания не пишутся — они подразумеваются.
public record Employee(String name, int employeeNumber) {
// Компактный конструктор
public Employee {
if (employeeNumber < 0) {
throw new IllegalArgumentException("Employee number cannot be negative");
}
// name и employeeNumber автоматически присвоятся после этого блока
}
}2. Дополнительные методы
Вы можете объявлять свои методы.
public record Employee(String name, int employeeNumber) {
// Дополнительный метод экземпляра
public String getEmail() {
return name.toLowerCase().replace(" ", ".") + "@company.com";
}
// Статический метод
public static Employee createCEO() {
return new Employee("The Boss", 1);
}
}⚠️ Важные ограничения
Record неизменяемы. Нельзя добавить
setterили изменить поле после создания.Не могут наследоваться. Все записи неявно являются
finalклассами.Не могут наследовать другие классы (кроме
java.lang.Record).Нельзя добавлять не-static поля экземпляра. Только те, что объявлены в заголовке.
Когда использовать Record?
DTO (Data Transfer Objects) — идеально для передачи данных между слоями приложения, например, из REST-контроллера.
Кортежи и простые контейнеры данных.
Ключи в
Mapили элементы вSet, так как у них правильно реализованыequalsиhashCode.Возврат нескольких значений из метода.
Вывод
Record в Java — это не просто синтаксический сахар. Это фундаментальное улучшение языка, которое делает код:
Короче и чище.
Безопаснее благодаря неизменяемости.
Проще для чтения и поддержки.
Если вы до сих пор не используете записи — самое время начать! Это изменит ваш подход к созданию классов-данных.