Быстрый тур по новым, готовым к работе функциям при обновлении с Java 11 до Java 17.
Через три года после Java 11 - на данный момент последней версии с долгосрочной поддержкой (LTS), Java 17 LTS будет выпущена в сентябре 2021 года. Пришло время сделать краткий обзор новых функций, которыми разработчики могут пользоваться после обновления с 11 до 17. Обратите внимание, что было внесено гораздо больше улучшений - в этой статье основное внимание уделяется тем функциям, которые могут напрямую использоваться большинством разработчиков:- Switch выражения (JEP 361)
- Текстовые блоки (JEP 378)
- Инструмент для упаковки (JEP 392)
- Сопоставление с образцом для instanceof (JEP 394)
- Записи (JEP 395)
- Запечатанные классы (JEP 409)
Switch выражения
Теперь switch может возвращать значение, как и выражение:// assign the group of the given planet to a variable
String group = switch (planet) {
case MERCURY, VENUS, EARTH, MARS -> "inner planet";
case JUPITER, SATURN, URANUS, NEPTUNE -> "outer planet";
};
Если правая часть одного case требует большего количества кода, его можно записать внутри блока, а значение возвращается с помощью yield:
// print the group of the given planet, and some more info,
// and assign the group of the given planet to a variable
String group = switch (planet) {
case EARTH, MARS -> {
System.out.println("inner planet");
System.out.println("made up mostly of rock");
yield "inner";
}
case JUPITER, SATURN -> {
System.out.println("outer planet");
System.out.println("ball of gas");
yield "outer";
}
};
Однако switch с использованием новых меток со стрелками не требует возврата значения, как и void выражение:
// print the group of the given planet
// without returning anything
switch (planet) {
case EARTH, MARS -> System.out.println("inner planet");
case JUPITER, SATURN -> System.out.println("outer planet");
}
По сравнению с традиционным переключателем, новое Switch выражение
- Использует «->» вместо «:»
- Позволяет использовать несколько констант для каждого case
- Не имеет сквозной семантики (т. е. не требует break)
- Делает переменные, определенные внутри ветви case, локальными для этой ветви
- Все возможные значения перечислены как case (как в приведенном выше перечислении, состоящем из восьми планет), или
- Должна быть предоставлена ветка «default».
Текстовые блоки
Текстовые блоки позволяют писать многострочные строки, содержащие двойные кавычки, без использования \n или \" escape-последовательностей:String block = """
Multi-line text
with indentation
and "double quotes"!
""";
Текстовый блок открывается тремя двойными кавычками, """за которыми следует разрыв строки, и закрывается тремя двойными кавычками.
Компилятор Java использует интеллектуальный алгоритм для удаления начального пробела из результирующей строки, чтобы:
- отступ, необходимый только для лучшей читаемости исходного кода Java, был удален.
- отступ, относящийся к самой строке, остлтся нетронутым
Multi-line.text
.with.indentation
..and."double.quotes"!
Представьте себе вертикальную полосу, охватывающую по высоте весь текстовый блок, перемещающуюся слева направо и удаляющую пробелы, пока она не коснется первого не пробельного символа. Ограничитель закрывающего текстового блока также считается, поэтому переместите его на две позиции влево.
String block = """
Multi-line text
with indentation
and "double quotes"!
""";
Результат представлен в следующей строке:
..Multi-line.text
...with.indentation
....and."double.quotes"!
Кроме того, из каждой строки удаляется конечный пробел, чего можно избежать, используя новую escape-последовательность \s.
Разрывы строк внутри текстовых блоков можно экранировать:
String block = """
No \
line \
breaks \
at \
all \
please\
""";
Результатом является следующая строка без разрывов строк:
No.line.breaks.at.all.please
В качестве альтернативы последний разрыв строки также можно удалить, добавив закрывающий разделитель непосредственно в конец строки:
String block = """
No final line break
at the end of this string, please""";
Вставка переменных в текстовый блок может выполняться как обычно с помощью статического метода String::format или с помощью нового метода экземпляра String::formatted, который немного короче для записи:
String block = """
%s marks the spot.
""".formatted("X");
Инструмент для упаковки
Предположим, у вас есть JAR-файл demo.jar в каталоге lib вместе с дополнительными JAR-файлами зависимостей. Следующая команда:jpackage --name demo --input lib --main-jar demo.jar --main-class demo.Main
упаковывает это демонстрационное приложение в собственный формат, соответствующий вашей текущей платформе:
- Linux: deb или rpm
- Windows: msi или exe
- macOS: pkg или dmg
Кросс-компиляция не поддерживается: если вам нужен пакет для пользователей Windows, вы должны создать его с помощью jpackage на машине Windows.
Создание пакета можно настроить с помощью многих других параметров, которые задокументированы на странице руководства jpackage.
Сопоставление с образцом для Instanceof
Сопоставление с образцом (Pattern matching) для instanceof позволяет исключить шаблонный код для выполнения приведений после сравнения типов:Object o = "string disguised as object";
if (o instanceof String s) {
System.out.println(s.toUpperCase());
}
В приведенном выше примере область действия новой переменной s интуитивно ограничена if веткой. Чтобы быть точным, переменная находится в области видимости, в которой гарантировано совпадение шаблона, что также делает следующий код допустимым:
if (o instanceof String s && !s.isEmpty()) {
System.out.println(s.toUpperCase());
}
А также наоборот:
if (!(o instanceof String s)) {
throw new RuntimeException("expecting string");
}
// s is in scope here!
System.out.println(s.toUpperCase());
Записи
Записи (Records) сокращают шаблонный код для классов, которые являются простыми носителями данных:record Point(int x, int y) { }
Эта строка кода в результате приводит к созданию класса записи, в котором автоматически определены:
- поля для x и y (как private и final)
- канонический конструктор для всех полей
- геттеры для всех полей
- equals, hashCode и toString (с учетом всех полей)
Point p = new Point(1, 2);
// getters - without "get" prefix
p.x();
p.y();
// equals / hashCode / toString
p.equals(new Point(1, 2)); // true
p.hashCode(); // depends on values of x and y
p.toString(); // Point[x=1, y=2]
Некоторые из наиболее важных ограничений классов записей заключаются в том, что они:
- неизменяемы (поскольку их поля являются private и final)
- неявно final
- невозможно определить дополнительные поля экземпляра
- всегда наследует от Record класса
- определить дополнительные методы
- реализовать интерфейсы
- кастомизировать канонический конструктор и аксессоры
// explicit canonical constructor
Point {
// custom validations
if (x < 0 || y < 0)
throw new IllegalArgumentException("no negative points allowed");
// custom adjustments (usually counter-intuitive)
x += 1000;
y += 1000;
// assignment to fields happens automatically at the end
}
// explicit accessor
public int x() {
// custom code here...
return this.x;
}
}
Кроме того, внутри метода можно определить локальную запись:
public void withLocalRecord() {
record Point(int x, int y) { };
Point p = new Point(1, 2);
}
Sealed классы
Sealed (запечатанный) класс явно перечисляет допустимые прямые подклассы. Другие классы не могут наследовать от этого класса:public sealed class Parent
permits ChildA, ChildB, ChildC { ... }
Точно так же запечатанный интерфейс явно перечисляет разрешенные прямые субинтерфейсы и реализующие классы:
sealed interface Parent
permits ChildA, ChildB, ChildC { ... }
Классы или интерфейсы в permits списке должны находиться в одном пакете (или в том же модуле, если родитель находится в названном модуле).
permits список может быть опущена, если подклассы (или интерфейсы) расположены в том же файле:
public sealed class Parent {
final class Child1 extends Parent {}
final class Child2 extends Parent {}
final class Child3 extends Parent {}
}
Каждый подкласс или интерфейс в permits списке должен использовать только один из следующих модификаторов:
- final (запрещает дальнейшее наследование; только для подклассов, поскольку интерфейсы не могут быть final)
- sealed (допускает дальнейшее, ограниченное наследование)
- non-sealed (снова разрешает неограниченное наследование)
Новые возможности в Java версий 12 — 17
Быстрый тур по новым, готовым к работе функциям при обновлении с Java 11 до Java 17. Через три года после Java 11 - на данный момент последней версии с долгосрочной поддержкой (LTS), Java 17 LTS будет...
habr.com