Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Шаблоны записей

Шаблон записи (record pattern) не только проверяет, что объект — запись нужного типа, но и сразу разбирает её на компоненты. Вместо вызова point.x() и point.y() вы получаете готовые переменные x и y. Шаблоны записей появились в Java 21 и работают в instanceof и switch.

Разбор записи

После имени записи в скобках перечисляют шаблоны для её компонентов — в том же порядке, что и в объявлении записи:

record Point(int x, int y) {}
 
void main() {
    Object value = new Point(3, 4);
 
    if (value instanceof Point(int x, int y)) {
        IO.println("x = " + x + ", y = " + y);
    }
}

Результат:

x = 3, y = 4

Имена переменных в шаблоне могут отличаться от имён компонентов: важен только порядок и тип.

В switch

Особенно выразительно шаблоны записей выглядят в switch. Каждая ветка сразу получает данные нужной фигуры:

sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
 
String describe(Shape shape) {
    return switch (shape) {
        case Circle(double r) -> "круг радиусом " + r;
        case Rectangle(double w, double h) when w == h -> "квадрат со стороной " + w;
        case Rectangle(double w, double h) -> "прямоугольник " + w + " на " + h;
    };
}
 
void main() {
    IO.println(describe(new Circle(2)));
    IO.println(describe(new Rectangle(3, 3)));
    IO.println(describe(new Rectangle(2, 5)));
}

Результат:

круг радиусом 2.0
квадрат со стороной 3.0
прямоугольник 2.0 на 5.0

Условие when работает и здесь: оно может использовать переменные, полученные при разборе.

Такое сочетание — запечатанный интерфейс, записи и switch с шаблонами — называют программированием, ориентированным на данные (data-oriented programming). Данные описываются простыми неизменяемыми записями, а операции над ними собраны в одном switch. Если добавить новый вид фигуры, компилятор сам покажет каждый switch, который нужно дополнить.

var в шаблоне

Чтобы не повторять типы компонентов, вместо них можно написать var — компилятор выведет тип сам:

record Order(String customer, double amount) {}
 
void main() {
    Object value = new Order("Анна", 49.90);
 
    if (value instanceof Order(var customer, var amount)) {
        IO.println(customer + " заплатила €" + amount);
    }
}

Результат:

Анна заплатила €49.9

Вложенные шаблоны

Если компонент записи — тоже запись, шаблоны можно вкладывать друг в друга и разбирать объект на любую глубину:

record Point(int x, int y) {}
record Line(Point start, Point end) {}
 
void main() {
    Object value = new Line(new Point(0, 0), new Point(3, 4));
 
    if (value instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
        double length = Math.hypot(x2 - x1, y2 - y1);
        IO.println("Длина отрезка: " + length);
    }
}

Результат:

Длина отрезка: 5.0

Без шаблонов пришлось бы писать line.end().x() - line.start().x() и так далее.

Безымянная переменная

Если какой-то компонент не нужен, вместо шаблона пишут _ — безымянную переменную (Java 22). Так видно, что значение пропущено намеренно:

record Order(String customer, double amount, String city) {}
 
void main() {
    List<Order> orders = List.of(
            new Order("Анна", 20.0, "Рим"),
            new Order("Олег", 35.5, "Осло"));
 
    for (Order order : orders) {
        if (order instanceof Order(var name, _, var city)) {
            IO.println(name + " из города " + city);
        }
    }
}

Результат:

Анна из города Рим
Олег из города Осло

Частая ошибка

Шаблон должен перечислять все компоненты записи. Если пропустить хотя бы один, программа не скомпилируется:

record Point(int x, int y) {}
 
void main() {
    Object value = new Point(3, 4);
 
    if (value instanceof Point(int x)) {
        IO.println(x);
    }
}

Результат:

Main.java:6: error: incorrect number of nested patterns
    if (value instanceof Point(int x)) {
                         ^
  required: int,int
  found: int
1 error
error: compilation failed

Ненужные компоненты не пропускают, а заменяют на _: Point(int x, _).

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.