Вопросы 221-230 - Records
Ответ: Record (запись) — особый вид класса для неизменяемых данных, появился в Java 16 (в 14 и 15 был в режиме preview). Вы перечисляете компоненты, а компилятор сам генерирует конструктор, методы доступа, equals(), hashCode() и toString().
// Раньше: около 30 строк
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; }
public int y() { return y; }
// + equals, hashCode, toString
}
// Теперь
public record Point(int x, int y) { }
Point p = new Point(1, 2);
System.out.println(p); // Point[x=1, y=2]
System.out.println(p.x()); // 1
Ответ:
| Что | Детали |
|---|---|
| Поля | private final поле для каждого компонента |
| Канонический конструктор | Принимает все компоненты в порядке объявления |
| Методы доступа | Называются как компонент: p.x(), а не getX() |
equals() / hashCode() |
Два record равны, если они одного типа и все компоненты равны |
toString() |
Формат Point[x=1, y=2] |
Любой из этих методов можно переопределить вручную.
Point a = new Point(1, 2);
Point b = new Point(1, 2);
System.out.println(a == b); // false (разные объекты)
System.out.println(a.equals(b)); // true (равны по значению)
Ответ: Компактный конструктор объявляется без списка параметров и нужен для проверки или нормализации значений. Присваивание полям компилятор добавляет сам в конце.
public record Range(int from, int to) {
public Range { // без скобок и параметров
if (from > to) {
throw new IllegalArgumentException("from > to");
}
// можно изменить параметры до автоматического присваивания:
// from = Math.max(from, 0);
}
}
new Range(5, 1); // IllegalArgumentException
this.from = from — ошибка компиляцииthis(...)
public record Range(int from, int to) {
public Range(int to) {
this(0, to); // делегирует каноническому
}
}
Ответ:
final — наследоваться от него нельзяjava.lang.Record)final — менять их нельзя, сеттеров нетabstractrecord Pair<A, B>(A first, B second) { }
public record Circle(double radius) implements Shape {
static final double PI = Math.PI; // статическое поле можно
public double area() { // метод можно
return PI * radius * radius;
}
// private int cache; // ОШИБКА: экземплярное поле
}
Ответ: Нет, он неглубоко неизменяем (shallowly immutable). Ссылки на компоненты изменить нельзя, но если компонент — изменяемый объект, то его содержимое менять можно.
record Team(String name, List<String> members) { }
List<String> list = new ArrayList<>(List.of("Ann"));
Team t = new Team("A", list);
list.add("Bob");
System.out.println(t.members()); // [Ann, Bob] — record «изменился»
record Team(String name, List<String> members) {
Team {
members = List.copyOf(members); // неизменяемая копия, null-элементы запрещены
}
}
Ответ:
| Критерий | Record | Lombok @Value |
|---|---|---|
| Реализация | Возможность языка | Внешняя библиотека с процессором аннотаций |
| Имена аксессоров | name() |
getName() |
| Наследование | Невозможно | Класс final по умолчанию |
| Дополнительные поля | Запрещены | Разрешены |
| Pattern matching | Поддерживает деконструкцию (record patterns) | Нет |
| Зависимости | Не нужны | Нужен Lombok и поддержка в IDE |
Ответ:
Map: equals/hashCode уже корректныObject[] или PairMoney, Email, Coordinates@ConfigurationProperties
// локальный record в stream
record NameLen(String name, int len) { }
List<NameLen> result = names.stream()
.map(n -> new NameLen(n, n.length()))
.filter(r -> r.len() > 3)
.toList();
@Entity) — им нужны конструктор без аргументов, не-final класс и изменяемые поляОтвет: Record pattern позволяет в instanceof и switch одновременно проверить тип и достать компоненты. В финальном виде появился в Java 21.
record Point(int x, int y) { }
Object o = new Point(3, 4);
// до Java 21
if (o instanceof Point p) {
System.out.println(p.x() + p.y());
}
// record pattern
if (o instanceof Point(int x, int y)) {
System.out.println(x + y); // 7
}
record Line(Point from, Point to) { }
static int dx(Object o) {
if (o instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
return x2 - x1;
}
return 0;
}
Ответ:
Serializable):writeObject/readObject игнорируются
record Point(int x, int y) { }
Point.class.isRecord(); // true
for (RecordComponent c : Point.class.getRecordComponents()) {
System.out.println(c.getName() + " : " + c.getType());
}
// x : int
// y : int
Field f = Point.class.getDeclaredField("x");
f.setAccessible(true);
f.set(new Point(1, 2), 5); // IllegalAccessException: поля record изменить нельзя
Class.getRecordComponents() и isRecord() появились в Java 16.
Ответ: Да. Можно переопределить любой аксессор, equals, hashCode и toString, но сигнатура аксессора должна совпадать (public, без параметров, тот же тип возврата).
record User(String name, String email) {
@Override
public String name() { // нормализуем при чтении
return name.trim();
}
@Override
public String toString() {
return "User(" + name + ")"; // не выводим email в логи
}
// производный метод, не компонент
public String domain() {
return email.substring(email.indexOf('@') + 1);
}
}
equals или hashCode, сохраняйте контракт: равные объекты должны иметь равные хеши. Обычно это не нужно — генерируемые версии уже верны.