Вышла Java 18

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

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

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

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

Паттерн-матчинг для switch, который появился в Java 17 в режиме preview, остаётся в этом статусе. В этом релизе присутствует два изменения по сравнению с предыдущей версией.

Первое изменение – константные паттерны теперь должны находиться выше, чем паттерны с условиями того же типа. Например, такой код успешно компилируется в Java 17:

switch (value) {
    case Integer i && bar() -> {}
    case 3 -> {}
    case Integer i -> {}
}

Но не компилируется в Java 18:

> javac --enable-preview --release 18 Main.java
Main.java:8: error: this case label is dominated by a preceding case label
            case 3 -> {}
                 ^

Чтобы код компилировался на Java 18, нужно переместить константный паттерн выше:

switch (value) {
    case 3 -> {}
    case Integer i && bar() -> {}
    case Integer i -> {}
}

Второе изменение – улучшенная проверка исчерпываемости для дженериков. Например, такой код не компилируется в Java 17, несмотря на то что switch покрывает все возможные случаи:

sealed interface Foo<T> { }
final class A<T> implements Foo<T> { }
final class B<T> implements Foo<T> { }
final class C implements Foo<String> { }

static int testGenericSealedExhaustive(Foo<Integer> foo) {
    return switch (foo) {
        case A<Integer> a -> 1;
        case B<Integer> b -> 2;
    };
}
javac --enable-preview --release 17 Main.java
Main.java:10: error: the switch expression does not cover all possible input values
    return switch (foo) {
           ^

В Java 18 же такой код успешно компилируется.

Сниппеты кода в документации Java API (JEP 413)

В JavaDoc теперь поддерживается новый тег @snippet, который указывает на то, что данный участок является примером кода.

Пример сниппета кода:

/**
 * A simple program.
 * {@snippet :
 *   public class HelloWorld {
 *     public static void main(String... args) {
 *       System.out.println("Hello World!"); // @highlight substring="println"
 *     }
 *   }
 * }
 */

Сгенерированная документация для него будет выглядеть примерно следующим образом:

public class HelloWorld {
  public static void main(String... args) {
    System.out.println("Hello World!");
  }
}

Тег @snippet введён в качестве замены существующему методу вставки примеров кода в JavaDoc с помощью блоков <pre>{@code ... }</pre>, который имеет ряд недостатков:

  • Тег @code не поддерживает возможность указания языка фрагмента, а значит теряется возможность проверки корректности кода, подсветки синтаксиса и ссылок, поскольку инструменты трактуют все такие фрагменты как простой текст.
  • Подсветку синтаксиса также нельзя добавить и вручную, поскольку внутренние HTML-теги будут трактоваться как часть фрагмента. Проблему можно решить через использование тега <pre>...</pre> (без @code), но тогда полностью исчезает указание того, что данный фрагмент документации является кодом.
  • Фрагменты кода часто являются неполными или содержат плейсхолдеры и многоточия. Тег @code никак не решает данную проблему.
  • Индентация строк во фрагменте остаётся такой, как написана в исходном коде.

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

  • В сниппете можно указать язык фрагмента с помощью атрибута lang: {@snippet lang=properties : ... }. Если язык не указан, то считается, что это Java. Проверка корректности пока ограничена только сбалансированностью фигурных скобок, но инструменты могут реализовать более сложную проверку.
  • Подсветка отдельных элементов фрагмента может быть сделана с помощью тега @highlight, который поддерживает три стиля bold, italic и highlighted. Все теги указываются в комментариях, поэтому полностью исчезают после генерации. Инструменты также имеют возможность сделать автоматическую подсветку синтаксиса.
  • Многоточия и неполные фрагменты кода возможны с помощью тега @replace. Фрагмент кода до замены плейсхолдеров остаётся синтаксически валидным кодом.
  • В сниппете можно сделать ссылки с помощью тега @link.
  • Общие отступы строк фрагмента съедаются при генерации документации.
  • При генерации HTML-документации стандартным доклетом сниппеты кода лучше выделяются на фоне остальной документации благодаря серому фону. Кроме того, каждый сниппет кода будет сопровождаться кнопкой копирования в буфер обмена.
  • Сниппеты кода могут быть внешними и подтягивать код из внешних файлов. Чтобы иметь возможность не включать весь файл в JavaDoc, поддерживаются регионы с помощью тега @region. Внешние сниппеты хороши тем, что внешние файлы могут быть предварительно проверены на корректность (например, если файлы Java не будут компилироваться, то и не смогут быть включены в JavaDoc).
UTF-8 по умолчанию (JEP 400)

UTF-8 теперь является кодировкой по умолчанию на всех платформах. Кодировка по умолчанию используется в таких API как java.io (InputStreamReader, FileReader, OutputStreamWriter, FileWriter, PrintStream), java.util (Formatter, Scanner), java.net (URLEncoder, URLDecoder). Кодировка в пакете java.nio.file осталась неизменной: в нём самого начала UTF-8 было в качестве Charset по умолчанию.

Если в Java 17 и более ранних версиях кодировка по умолчанию определялась при старте JVM и зависела от разных факторов (операционная система, пользовательская локаль, кодировка операционной системы), то с Java 18 она всегда UTF-8. Если нужно включить старый механизм установки кодировки при старте, то можно использовать опцию -Dfile.encoding=COMPAT. Кроме того, эту кодировку можно узнать, используя свойство native.encoding, которое появилось в Java 17.

Чтобы будущий переход на Java 18 был более гладким, рекомендуется стартовать свои приложения на Java 17 или более ранних версиях с опцией -Dfile.encoding=UTF-8.

Простой веб-сервер (JEP 408)

Появилась новая утилита jwebserver, которая запускает простой веб-сервер, предоставляющий статический доступ к указанной папке с файлами. По умолчанию используется текущая директория (если не указана опция -d), а порт равен 8000 (если не указана опция -p).

Веб-сервер поддерживает только HTTP/1.1, не поддерживает HTTPS и аутентификацию. Его рекомендуется использовать только для целей разработки и тестирования.

Вместе с утилитой jwebserver также появилось новое API, позволяющее запускать веб-сервер программно. За это отвечают новые классы SimpleFileServer, HttpHandlers и Request в пакете com.sun.net.httpserver.

Deprecate Finalization for Removal (JEP 421)

Финализация объектов, которая стала deprecated в Java 9, теперь стала deprecated for removal, то есть подлежит окончательному удалению.

Начиная с Java 18, все методы, относящиеся к финализации, помечены аннотацией @Deprecated(forRemoval=true). Это Object.finalize() и его наследники (часть из них удалены полностью), Runtime.runFinalization() и System.runFinalization().

Сама финализация всё ещё работает, однако появилась новая опция --finalization=disabled, которая её отключает.

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

Reimplement Core Reflection with Method Handles (JEP 416)

Механизм рефлексии в пакете java.lang.reflect теперь реализован поверх method handles в пакете java.lang.invoke. В Java 17 и раньше эти два механизма существовали независимо и имели разную внутреннюю реализацию. Такое дублирование усложняет поддержку платформы и внесение в неё новых языковых изменений.

Для разработчика такое изменение не должно иметь видимых последствий (кроме небольших различий в производительности). Однако на всякий случай предоставлен ключ, позволяющий включить старую реализацию: -Djdk.reflect.useDirectMethodHandle=false. Старая реализация будет полностью удалена в одной из следующих версий Java.

Internet-Address Resolution SPI (JEP 418)

Появилось новое SPI, позволяющее кастомизировать получение IP-адреса по имени хоста (и наоборот). По умолчанию InetAddress API делает нативный резолвинг, то есть делегированием операционной системе. Если нужен альтернативный механизм резолвинга, то теперь его можно предоставить путём реализации нового провайдера сервиса InetAddressResolverProvider.

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

  • Более производительный резолвинг, чем системный.
  • Неблокирующий резолвинг.
  • Альтернативный протокол DNS (например, DNS over QUIC).
  • Тестирование (подмена IP-адресов, моки).
Foreign Function & Memory API (Second Incubator) (JEP 419)

Foreign Function & Memory API, который появился в Java 17 в инкубационном статусе, остаётся в этом статусе в модуле jdk.incubator.foreign.

В Java 19 это API перестанет быть инкубационным и станет preview API в пакете java.lang.foreign.

Vector API (Third Incubator) (JEP 417)

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

Java 18 не является LTS-релизом и будет получать обновления только в течение полугода (до сентября 2022).

Подписывайтесь на канал в Telegram, чтобы не пропускать новости.

Все материалы на этом сайте выложены под лицензией CC BY-SA 4.0
© Евгений Козлов, 2017-2022
Feed