Питання 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. Що таке незмінний (immutability) об’єкт]]
  • [[3. Як створити незмінний клас в Java]]
  • [[26. Як реалізувати Builder pattern для незмінного класу]]
  • [[27. Чи можна використовувати незмінні об’єкти як ключі в HashMap]]
  • [[29. Як правильно працювати з колекціями в незмінних класах]]