Шаблоны в instanceof
Сопоставление с образцом (pattern matching) — способ одновременно проверить, подходит ли значение под какую-то форму, и сразу достать из него нужные части. В Java оно появилось постепенно, и первый шаг — шаблоны в операторе instanceof, доступные с Java 16.
Как было раньше
Оператор instanceof проверяет, принадлежит ли объект типу. Но после проверки объект всё равно приходилось вручную приводить к типу — писать название типа в скобках:
void main() {
Object value = "Привет";
if (value instanceof String) {
String text = (String) value;
IO.println(text.length());
}
}Результат:
6Тип String здесь упомянут трижды, хотя проверка одна.
Шаблон типа
С шаблоном проверка и приведение объединяются: после типа пишут имя новой переменной. Если проверка прошла, переменная уже имеет нужный тип:
void main() {
Object value = "Привет";
if (value instanceof String text) {
IO.println(text.length());
}
}Результат:
6Запись String text называется шаблоном типа, а text — переменной шаблона. Она появляется, только если объект действительно оказался строкой.
Разные типы
Шаблоны особенно удобны, когда значение может быть разных типов и с каждым нужно поступить по-своему:
String describe(Object value) {
if (value instanceof Integer number) {
return "целое число, в два раза больше: " + number * 2;
} else if (value instanceof String text) {
return "строка из " + text.length() + " символов";
} else if (value instanceof List<?> list) {
return "список из " + list.size() + " элементов";
}
return "что-то другое";
}
void main() {
IO.println(describe(21));
IO.println(describe("Олег"));
IO.println(describe(List.of(1, 2, 3)));
IO.println(describe(3.14));
}Результат:
целое число, в два раза больше: 42
строка из 4 символов
список из 3 элементов
что-то другоеДля таких цепочек есть ещё более удобный инструмент — шаблоны в switch, о них следующий урок.
Шаблон в условии
Переменную шаблона можно использовать дальше в том же условии после &&: правая часть выполняется, только если левая истинна, а значит, переменная уже есть:
void main() {
Object input = " ";
if (input instanceof String s && !s.isBlank()) {
IO.println("Непустая строка: " + s);
} else {
IO.println("Не строка или пустая строка");
}
}Результат:
Не строка или пустая строкаОтрицание
Если проверить «не является ли», переменная шаблона доступна после блока if — при условии, что блок завершает метод. Так удобно отсеивать неподходящие значения в начале метода:
void printUpper(Object value) {
if (!(value instanceof String text)) {
IO.println("Нужна строка");
return;
}
IO.println(text.toUpperCase());
}
void main() {
printUpper("анна");
printUpper(42);
}Результат:
АННА
Нужна строкаКомпилятор понимает: если мы дошли до последней строки, значит, value — строка.
Частая ошибка
С || переменная шаблона недоступна: правая часть выполняется как раз тогда, когда проверка типа не прошла, и переменной нет:
void main() {
Object value = 10;
if (value instanceof String s || s.isEmpty()) {
IO.println("Пусто");
}
}Результат:
Main.java:4: error: cannot find symbol
if (value instanceof String s || s.isEmpty()) {
^
symbol: variable s
location: class Main
1 error
error: compilation failedПеременная шаблона существует только там, где компилятор точно знает, что проверка прошла. Используйте её после && или внутри блока if.