Вышла Java 17

в 15:59, , рубрики: java, java11, java12, java13, java14, java15, java16, java17, java9, JDK, openjdk, pattern-match, sealed, simd, switch

Вышла общедоступная версия Java 17. В этот релиз попало более 2700 закрытых задач и 14 JEP'ов. Изменения API можно посмотреть по этой ссылке.

Ссылки на скачивание:

Перечислим JEP'ы, которые попали в Java 17.

Язык

Восстановление всегда строгой семантики чисел с плавающей точкой (JEP 306)

Теперь все выражения с плавающей точкой вычисляются строго на всех платформах, и разницы между строгой семантикой и семантикой по умолчанию больше нет (как это было до Java 1.2). Таким образом, ключевое слово strictfp больше не имеет эффекта, и при его использовании выдаётся предупреждение:

> javac Main.java
Main.java:1: warning: [strictfp] as of release 17, all floating-point
expressions are evaluated strictly and 'strictfp' is not required
strictfp public class Main {
                ^

sealed классы (JEP 409)

«Запечатанные» классы после двух preview итераций в Java 15 и Java 16 наконец-то стали стабильной синтаксической конструкцией и больше не требуют флага --enable-preview. Различий по сравнению с Java 16 нет.

Напомним, ключевым слово sealed можно пометить класс или интерфейс, если нужно ограничить список его наследников. Список можно указать явно, перечислив все наследники с помощью ключевого слова permits, либо он выведется неявно компилятором из всех задекларированных наследников в текущем исходном файле.

Паттерн-матчинг для switch (Preview) (JEP 406)

Появилась новая синтаксическая конструкция в режиме preview, существенно расширяющая возможности оператора switch: теперь оператор switch поддерживает не только простые тесты на равенство с константами примитивов, перечислений и строк, но и тесты по паттернам.

Простейшие паттерны это паттерны по типу:

Object o = ...
return switch (o) {
    case Integer i -> String.format("int %d", i);
    case Long l -> String.format("long %d", l);
    case Double d -> String.format("double %f", d);
    case String s -> String.format("String %s", s);
    default -> o.toString();
};

Паттерны могут снабжаться условиями путём добавления охранных паттернов:

Shape s = ...
switch (s) {
    case Triangle t && (t.calculateArea() > 100) ->
        System.out.println("Large Triangle");
    default ->
        System.out.println("A shape (possibly a small triangle)");
}

Также добавлена поддержка матчинга null с помощью отдельной метки null:

Object o = ...
switch (o) {
    case null -> System.out.println("Null");
    case String s -> System.out.println("String: " + s);
    default -> System.out.println("Other");
}

Ветки null и default можно объединять друг с другом:

String s = ...
switch (s) {
    case "Foo", "Bar" -> System.out.println("Foo or Bar");
    case null, default -> System.out.println("Null or other");
}

Все switch с паттернами должны быть исчерпывающими (даже если это switch statement, а не switch expression):

Object o = ...
switch (o) {
    case String s -> System.out.println("String: " + s);
    case Integer i -> System.out.println("Integer");
}

> javac --enable-preview --release 17 Main.java
Main.java:4: error: the switch statement does not cover all possible input values
        switch (o) {
        ^

Чтобы пример выше стал компилироваться, нужно либо добавить ветку default, либо тотальный паттерн (в данном случае case Object obj).

Старый switch statement можно продолжать делать неисчерпывающим, однако это может измениться в будущем.

Новый switch очень хорошо взаимодействует с sealed классами, поскольку круг подтипов является ограниченным, а это значит, что ветка default не является необходимой, если перечислены все возможные случаи:

sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {}

S s = ...
switch (s) {
    case A a -> System.out.println("A");
    case B b -> System.out.println("B");
    case C c -> System.out.println("C");
}

На этом беглое рассмотрение паттерн-матчинга для switch мы закончим, а про другие возможности и нюансы можно почитать на странице JEP.

API

Enhanced Pseudo-Random Number Generators (JEP 356)

В Java 17 появился новый пакет java.util.random с новыми интерфейсами и реализациями генераторов псевдослучайных чисел. Наверху иерархии новый интерфейс RandomGenerator, который является родителем как новых генераторов и специализаций (SplittableRandomGenerator, JumpableRandomGenerator, LeapableRandomGenerator, ArbitrarilyJumpableRandomGenerator), так и старых классов (Random, ThreadLocalRandom, SplittableRandom).

Таким образом, алгоритмы генерации чисел теперь стали более взаимозаменяемыми:

RandomGenerator random1 = new Random();
RandomGenerator random2 = ThreadLocalRandom.current();
RandomGenerator random3 = RandomGenerator.getDefault();
RandomGenerator random4 = RandomGenerator.of("Xoshiro256PlusPlus");

Кроме того, список генераторов ограничен не только теми, что предоставлены самим JDK, но является расширяемыем: предоставлять свои или сторонние реализации можно через механизм ServiceLoader.

Deprecate the Applet API for Removal (JEP 398)

API аплетов, которое стало deprecated в Java 9, теперь стало помечено как подлежащее окончательному удалению. Таким образом, существующий код, который использует классы из Applet API (java.applet.Applet, javax.swing.JApplet и т.д.), может перестать работать на одной из следующих версий Java (но всё ещё продолжит работать на Java 17).

Удаление RMI Activation (JEP 407)

Механизм RMI Activation, который стал deprecated в Java 15, теперь удалён окончательно. RMI Activation был частью RMI, которая использовалась в чрезвычайно малом количестве приложений и фактически устарела. Для того чтобы избежать дорогой поддержки никому не нужного механизма, было принято решение его полностью удалить.

Deprecate the Security Manager for Removal (JEP 411)

Security Manager стал помечен как подлежащий удалению. В одной из будущих версий его планируется удалить окончательно. Аннотацию @Deprecated(forRemoval=true) получили 10 классов и 10 методов из стандартной библиотеки Java. Среди них java.lang.SecurityManager, java.security.Policy, java.lang.System::setSecurityManager, java.lang.System::getSecurityManager, java.lang.Thread::checkAccess и другие. Кроме того, при вызове метода System.setSecurityManager() во время исполнения будет выдаваться предупреждение:

WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by com.foo.bar.Server
  (file:/tmp/foobarserver/thing.jar)
WARNING: Please consider reporting this to the maintainers of com.foo.bar.Server
WARNING: System::setSecurityManager will be removed in a future release

Также предупреждение будет выдаваться при установке Security Manager через аргументы командной строки указанием свойства java.security.manager:

> java -Djava.security.manager=com.foo.bar.Server MyApp
WARNING: A command line option has enabled the Security Manager
WARNING: The Security Manager is deprecated and will be removed in a future release

Начать отказ от Security Manager было решено по причине того, что он слабо отвечает современным требованиям безопасности и всё меньше и меньше пользуется спросом при разработке новых Java-приложений, но цена его поддержки остаётся очень высокой. У него хрупкая модель разрешений, сложная программная модель, и он приносит большие накладные расходы по производительности.

Foreign Function & Memory API (Incubator) (JEP 412)

Foreign Memory API, который побывал в Java 14, 15, 16 в инкубационном статусе, и Foreign Linker API, появившийся в Java 16, теперь объединены в один Foreign Function & Memory API, и он всё ещё остаётся в инкубационном статусе в модуле jdk.incubator.foreign.

Vector API (Second Incubator) (JEP 414)

Векторное API, которое появилось в Java 16 в инкубационном статусе, всё ещё остаётся в этом статусе в модуле jdk.incubator.vector.

Context-Specific Deserialization Filters (JEP 415)

Фильтры десериализации, которые появились в Java 9 для контроля десериализации из ненадёжных источников данных, стали более гибкими благодаря введению фабрики фильтров. Если раньше возможно было только указывать фильтр индивидуально для каждого объекта ObjectInputStream или использовать один глобальный статический фильтр для всей виртуальной машины, то сейчас можно указать статическую фабрику фильтров, которая для каждой операции десериализации будет возвращать нужный фильтр, подходящий для данного контекста.

Фабрику фильтров можно установить через метод ObjectInputFilter.Config::setSerialFilterFactory, либо через аргументы командной строки, передав имя класса фабрики в свойство jdk.serialFilterFactory.

JVM

New macOS Rendering Pipeline (JEP 382)

Реализован новый конвейер рендеринга на основе современного Apple Metal API, который был создан с целью заменить устаревший Apple OpenGL API. Новый конвейер сосуществует со старым, который пока что остаётся включённым по умолчанию, но если OpenGL не работает, то запускается новый. Таким образом, Java 17 полностью готова к запуску на будущих версиях macOS, где OpenGL API будет окончательно удалён. Также новый конвейер можно включить явно, указав опцию -Dsun.java2d.metal=true.

Порт под macOS/AArch64 (JEP 391)

OpenJDK портирован на архитектуру macOS/AArch64, и теперь список дистрибутивов OpenJDK и Oracle JDK пополнился сборками для Apple M1.

Строгая инкапсуляция внутренностей JDK (JEP 403)

Инкапсуляция внутренних API, которая была введена в Java 9 и была выключена по умолчанию в Java 16, но всё ещё могла быть включена одной опцией командной строки, теперь не может быть включена одной опцией:

> java --illegal-access=permit Main
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=permit;
  support was removed in 17.0

Таким образом, доступиться до внутренностей JDK (sun.*, com.sun.*, jdk.internal.* и т.д.) теперь можно только путём явного перечисления всех необходимых пакетов с помощью опций --add-opens или --add-exports.

Изменения не касаются критического API в модуле jdk.unsupported: классы в пакетах sun.misc и sun.reflect остаются доступными без флагов.

В будущем опция --illegal-access будет удалена окончательно, и при её использовании будет ошибка старта.

Удаление экспериментальных компиляторов AOT и JIT (JEP 410)

Удалены экспериментальные ahead-of-time и just-in-time Graal компиляторы. Такое решение было принято из-за сложности поддержки и их малой используемости (те, кому они нужны, используют GraalVM). По факту этих компиляторов уже не было в сборках JDK 16 от Oracle.

Удалению подлежали три модуля: jdk.aot, jdk.internal.vm.compiler и jdk.internal.vm.compiler.management. Модуль JVMCI (jdk.internal.vm.ci) остался, чтобы у разработчиков всё ещё оставалась возможность использовать внешний JIT-компилятор.

Заключение

Предыдущая версия OpenJDK с Long Term Support, Java 11, вышла ровно 3 года назад, хотя кажется, что она вышла совсем недавно, практически вчера. Но вот уже вышло 5 версий (12, 13, 14, 15, 16) и мы не успели опомниться, как подоспела следующая LTS-версия, Java 17.

Для тех, кто будет переходить с 11, Java 17 будет достаточно большим апгрейдом. Если сложить все релизы с 12 по 17, то в сумме появилось 5 больших языковых конструкций: выражения switch, блоки текста, записи, паттерн-матчинг для instanceof и sealed классы. Появилось очень много интересного API, сделано много улучшений производительности, оптимизировано потребление памяти, уменьшено время старта JVM. Очень много чего было удалено, поэтому могут возникнуть некоторые трудности при миграции.

Следующими большими шагами для Java будет завершение реализации проектов Valhalla, Loom и Panama, а также дальнейшие языковые улучшения в проекте Amber. Увидим ли мы всё это в следующей LTS-версии? Узнаем через 2 года.

Автор: Zheka Kozlov

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js