Урок по треку Java 7 мин

Современная Java: record, sealed, pattern matching

Что нового с Java 8 по 21: var, record, sealed, pattern matching, switch-выражения, текстовые блоки.

Большинство вакансий в 2026 году требуют Java 17 или 21, а новые проекты стартуют на 21 или 25 (обе LTS). Интервьюер ждёт, что вы пишете на современной Java, а не на «Java 8 с var».

Карта версий

ВерсияЧто важно для собеса
8лямбды, Stream API, Optional, java.time
10var для локальных переменных
11 (LTS)HttpClient, String.isBlank/strip/repeat
14switch-выражения со стрелками
15текстовые блоки """
16record, pattern matching для instanceof
17 (LTS)sealed-классы
21 (LTS)pattern matching в switch, record patterns, виртуальные потоки, SequencedCollection
22безымянные переменные _
25 (LTS)компактные исходники и void main() без класса, код до super(...) в конструкторе

var

var list = new ArrayList<String>(); тип выводится компилятором и фиксируется, это не динамическая типизация. Только для локальных переменных с инициализатором (и в for, лямбдах). Нельзя для полей, параметров методов и var x = null.

record

public record Money(BigDecimal amount, String currency) {
    public Money {                       // компактный конструктор
        if (amount.signum() < 0) throw new IllegalArgumentException("amount < 0");
    }
    public Money plus(Money o) { return new Money(amount.add(o.amount), currency); }
}

Record это неизменяемый носитель данных: поля private final, канонический конструктор, методы-аксессоры amount() (без get), equals, hashCode, toString по всем компонентам. Record неявно final и наследует java.lang.Record, поэтому не может расширять другой класс, но может реализовывать интерфейсы. Дополнительные поля экземпляра объявлять нельзя, статические можно. Неизменяемость поверхностная: record R(List<String> items) хранит ссылку на изменяемый список, поэтому в компактном конструкторе делают items = List.copyOf(items).

Как JPA-сущность record не годится (нужен конструктор без аргументов и изменяемость), а как DTO и ключ HashMap идеален.

sealed

public sealed interface Shape permits Circle, Square, Rect {}
public record Circle(double r) implements Shape {}
public record Square(double side) implements Shape {}
public non-sealed class Rect implements Shape { ... }

Набор наследников закрыт: каждый должен быть final, sealed или non-sealed. Компилятор знает все варианты, и это делает switch исчерпывающим.

Pattern matching

if (obj instanceof String s && !s.isBlank()) {   // Java 16
    System.out.println(s.length());
}

double area = switch (shape) {                    // Java 21
    case Circle c -> Math.PI * c.r() * c.r();
    case Square(double side) -> side * side;      // record pattern
    case Rect r when r.isEmpty() -> 0;            // guard
    case Rect r -> r.width() * r.height();
};

Для sealed-иерархии default не нужен: если добавить новый класс в permits, все такие switch перестанут компилироваться, и вы найдёте каждое место, где надо его обработать. case null теперь можно обработать явно, иначе switch по null бросает NPE.

switch-выражения и текстовые блоки

Стрелочный switch не проваливается (нет break), может возвращать значение, а yield возвращает значение из блока. Текстовый блок """ убирает общий отступ и удобен для SQL и JSON в коде.

Частые ошибки

  • Считать, что var делает переменную динамической.
  • Делать record с изменяемыми коллекциями без копирования.
  • Писать default в switch по sealed-типу и терять проверку исчерпываемости.
  • Использовать record как JPA-сущность.

Что спросят на собесе

  • На какой версии Java пишете, что из 17 и 21 используете?
  • Что генерирует record и чем он отличается от класса с Lombok @Value?
  • Зачем нужны sealed-классы и как они работают со switch?
  • Что такое record patterns и guard в switch?
  • Где нельзя использовать var?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.