Java
March 22, 2023

Вышла Java 20

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

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

Вот список JEP'ов, которые попали в Java 20.

Паттерны записей (Second Preview) (JEP 432)

В паттерны записей, которые появились в Java 19 в режиме preview (и остающиеся в этом статусе в Java 20), было внесено три главные изменения.

Во-первых, добавилась поддержка вывода типов в записях-дженериках:

record Box<T>(T t) {}

static void test(Box<String> box) {
    if (box instanceof Box(var s)) { // Выводится Box<String>(var s)
        System.out.println("String " + s);
    }
}

Во-вторых, паттерны записей теперь могут присутствовать в заголовке улучшенного цикла for:

record Point(int x, int y) {}

static void dump(Point[] pointArray) {
    for (Point(var x, var y) : pointArray) {
        System.out.println("(" + x + ", " + y + ")");
    }
}

Примечание: эту возможность было решено удалить в Java 21, но она снова может появиться в будущем в другом JEP'е.

В-третьих, исчезла поддержка именованных паттернов записей. Это значит, что такой код, который компилировался в Java 19, в Java 20 уже не будет компилироваться:

if (obj instanceof Point(var x, var y) p) { // Syntax error
    ...
}

Хотя именованные паттерны и исчезли в этом релизе, это не значит, что они исчезли насовсем. Возможно они появятся в одном из будущих релизов, когда будут более тщательно продуманы.

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

Это уже четвёртая итерация preview паттерн-матчинга в Java. Напомним, что предыдущие три попали в Java 17, 18 и 19. В новой версии три главных изменения.

Во-первых, исчерпывающий switch по перечислениям теперь выбрасывает MatchException, а не IncompatibleClassChangeError, если ни одна из меток switch не сматчилась.

Во-вторых, упростилась грамматика меток switch в JLS.

В-третьих, добавилась поддержка вывода типов в записях-дженериках, если они являются паттернами в switch:

record Pair<S, T>(S first, T second) {}

static void recordInference(Pair<String, Integer> pair) {
    switch (pair) {
        case Pair(var fst, var snd) -> ... // Выводится Pair<String, Integer>
        ...
    }
}

Как видите, здесь JEP 433 полностью согласуется с JEP 432.

Паттерны записей и паттерн-матчинг для switch предлагается финализировать в Java 21 (JEP 440 и JEP 441).

Virtual Threads (Second Preview) (JEP 436)

Виртуальные потоки, которые появились в Java 19, продолжают оставаться в статусе Preview API (предлагается финализировать их в Java 21). Изменений API, связанных с проектом Loom, в этом релизе нет (если не считать scoped values и structured concurrency, которые имеют инкубационный статус).

Scoped Values (Incubator) (JEP 429)

Появился новый класс ScopedValue, который позволяет обмениваться иммутабельными данными без их передачи через аргументы методов. Он является альтернативой существующему классу ThreadLocal.

Классы ThreadLocal и ScopedValue похожи тем, что решают одну и ту же задачу: передать значение переменной в рамках одного потока (или дерева потоков) из одного места в другое без использования явного параметра. В случае ThreadLocal для этого вызывается метод set(), который кладёт значение переменной для данного потока, а потом метод get() вызывается из другого места для получения значения переменной. У данного подхода есть ряд недостатков:

  • Неконтролируемая мутабельность (set() можно вызвать когда угодно и откуда угодно).
  • Неограниченное время жизни (переменная очистится, только когда завершится исполнение потока или когда будет вызван ThreadLocal.remove(), но про него часто забывают).
  • Высокая цена наследования (дочерние потоки всегда вынуждены делать полную копию переменной, даже если родительский поток никогда не будет её изменять).

Эти проблемы усугубляются с появлением виртуальных потоков, которые могут создаваться в гораздо больше количестве, чем обычные.

ScopedValue лишён вышеперечисленных недостатков. В отличие от ThreadLocal, ScopedValue не имеет метода set(). Значение ассоциируется с объектом ScopedValue путём вызова другого метода where(). Далее вызывается метод run(), на протяжении которого это значение можно получить (через метод get()), но нельзя изменить. Как только исполнение метода run() заканчивается, значение отвязывается от объекта ScopedValue. Поскольку значение не меняется, решается и проблема дорогого наследования: дочерним потоком не надо копировать значение, которое остаётся постоянным в течение периода жизни.

Пример использования ScopedValue:

static final ScopedValue<Credentials> CREDENTIALS = new ScopedValue<>();

Credentials creds = ...
ScopedValue.where(CREDENTIALS, creds).run(() -> {
   ...
   Connection connection = connectDatabase();
   ...
});

Connection connectDatabase() {
   Credentials credentials = CREDENTIALS.get();
   ...
}

Во многих случаях ScopedValue будет являться предпочтительной заменой ThreadLocal. Однако когда иммутабельный подход неприменим для решения задачи, ThreadLocal может остаться предпочтительным.

На период инкубации новое API будет находиться в модуле jdk.incubator.concurrent.

В Java 21 scoped values, скорее всего, станут preview.

Structured Concurrency (Second Incubator) (JEP 437)

Structured concurrency, которое появилось в Java 19, остаётся в инкубационном статусе в модуле jdk.incubator.concurrent (вместе со ScopedValue).

Единственное отличие от предыдущей версии API – это то, что StructuredTaskScope теперь поддерживает наследование scoped values потоками, созданными внутри области видимости задачи.

Foreign Function & Memory API (Second Preview) (JEP 434)

Foreign Function & Memory API, ставшее preview в Java 19, продолжает находиться в этом статусе. API находится в пакете java.lang.foreign.

Основные изменения в этом релизе:

  • Исчез интерфейс MemoryAddress. Теперь адреса моделируются через MemorySegment с нулевой длиной.
  • Улучшена иерархия sealed интерфейса MemoryLayout, чтобы лучше соответствовать паттерн-матчингу для switch.
  • Исчез интерфейс MemorySession. Он разделён на два интерфейса Arena и SegmentScope.

В Java 21 Foreign Function & Memory API останется на третье preview.

Vector API (Fifth Incubator) (JEP 438)

Векторное API всё никак не хочет становиться стабильным и остаётся в инкубационном статусе уже в пятый раз (модуль jdk.incubator.vector). В этом релизе лишь небольшие исправления багов и улучшения производительности. Скорее всего, инкубационный статус будет оставаться до тех пор, пока необходимые фичи проекта Valhalla не появятся в режиме preview (проект Panama сильно зависит от проекта Valhalla).

Заключение

Java 20 не является LTS-релизом и будет получать обновления от Oracle только в течение полугода (до сентября 2023 года). LTS-релизом станет следующая версия, Java 21.

Источник