openjdk.ruOpenJDK на русском

JEP 12: Preview Features

Возможности в статусе Preview (предварительная версия)

ОтветственныйAlex Buckley
ТипProcess
ОбластьSE
СтатусActive
Обсуждениеjdk dash dev at openjdk dot java dot net
ТрудоёмкостьM
ДлительностьM
РецензентыAlan Bateman, Brian Goetz, Mark Reinhold
ОдобренMark Reinhold
Создан2018/01/19 01:27
Обновлён2026/07/02 17:47
Задача8195734

Аннотация

Возможность в статусе Preview — это новая возможность языка Java, Java Virtual Machine или Java SE API, которая полностью специфицирована, полностью реализована и всё же не является постоянной. Она доступна в выпуске JDK типа Feature Release (функциональный выпуск), чтобы получить отзывы разработчиков на основе реального использования; в результате она может стать постоянной в одной из будущих версий платформы Java SE.

Цели

  • Позволить разработчикам платформы Java сообщать, появится ли новая возможность «в Java» примерно в нынешнем виде в течение ближайших 12 месяцев.

  • Определить модель разделения новых возможностей языка, VM и API в зависимости от того, являются ли они постоянными или непостоянными в платформе Java SE (то есть будут ли они существовать в том же виде во всех будущих выпусках либо будут существовать в другом виде или не будут существовать вовсе).

  • Донести намерение: код, использующий Preview-возможности из более старого выпуска платформы Java SE, не обязательно будет компилироваться или запускаться в более новом выпуске.

  • Описать соотношение между Preview-возможностями, с одной стороны, и возможностями HotSpot в статусе Experimental (экспериментальная функция) / возможностями API в статусе Incubator (инкубационный модуль), с другой.

Что не является целью

  • Не требуется, чтобы все новые возможности языка, VM и API изначально были доступны как Preview-возможности.

  • Этот JEP не обязан предписывать конкретные механизмы сбора и оценки отзывов разработчиков о Preview-возможностях.

  • Ничто в этом JEP не следует толковать как поощрение или разрешение фрагментации платформы Java SE.

История

  • Этот JEP появился в 2018 году, примерно во время JDK 12, чтобы ввести в платформе Java SE два вида Preview-возможностей: Preview-возможности языка и Preview-возможности VM. Preview-возможности присутствуют во всех компиляторах Java и реализациях JVM, но по умолчанию отключены. JEP описал свойства дизайна, обязательные для всех Preview-возможностей, и задал правила, по которым разработчики включают и используют Preview-возможности во время компиляции и во время выполнения. JEP также рассматривал, как Preview-возможности представлены в процессе JEP, который регулирует всю разработку возможностей JDK.

  • В 2019 году, примерно во время JDK 13, этот JEP был дополнен: в нём было признано, что Preview-возможности языка часто разрабатываются вместе с новыми API в модуле java.base, прежде всего с новыми классами в java.lang. Это привело к классификации совместно разрабатываемых API: «основные» (essential), «рефлексивные» (reflective) и «вспомогательные» (convenient).

  • В 2020 году, примерно во время JDK 15, этот JEP был дополнен, чтобы разрешить в платформе Java SE третий вид Preview-возможностей: Preview-API. Preview-API вобрали в себя идею API, разрабатываемых вместе с Preview-возможностями языка, и, кроме того, позволили определять API Java SE, не связанные с другими Preview-возможностями; это привело к классификации Preview-API на «основные», «рефлексивные», «вспомогательные» и «самостоятельные» (standalone). JEP сформулировал Preview-API так, чтобы у них были те же свойства, что и у Preview-возможностей языка и VM, и чтобы разработчики включали их точно так же. Однако JEP также признал различия между четырьмя категориями Preview-API, а также между Preview-API и Preview-возможностями языка, которые обусловили более мягкие правила использования Preview-API, чем Preview-возможностей языка и VM.

  • В 2023 году, примерно во время JDK 20, этот JEP был изменён, чтобы задать ожидания, что (1) период Preview для API может приводить к изменениям, качественно отличающимся от изменений возможностей языка или VM, и (2) при определённых обстоятельствах период Preview может длиться дольше 12 месяцев. Предысторию см. в ретроспективе Preview Features: A Look Back, and A Look Ahead.

Мотивация

Платформа Java SE используется по всему миру, поэтому цена ошибки в дизайне возможности языка Java, возможности JVM или API Java SE высока. Ошибка может быть жёсткой технической ошибкой (например, изъяном в системе типов Java), мягкой проблемой удобства использования (например, неожиданным взаимодействием с более старой возможностью) или неудачным архитектурным решением (например, таким, которое закрывает направления для будущих возможностей).

Чтобы укрепить уверенность в корректности и полноте новой возможности — будь то в языке Java, в JVM или в API Java SE, — желательно, чтобы возможность в течение некоторого времени была широко доступна после того, как её спецификация и реализация стабилизировались, но до того, как она получит окончательный и постоянный статус в платформе Java SE. Чтобы обеспечить максимально широкий охват и повысить вероятность быстрых отзывов, возможность может быть включена в выпуск JDK типа Feature Release в статусе Preview. Выпуск возможности в статусе Preview в JDK побудит производителей инструментов обеспечить её хорошую поддержку до того, как основная масса разработчиков Java начнёт использовать её в рабочих системах.

На протяжении шести месяцев выпуска JDK типа Feature Release, а возможно, и следующего выпуска, сильные и слабые стороны возможности в «реальных условиях» будут оцениваться, чтобы решить, есть ли у неё долгосрочная роль в платформе Java SE. В конечном итоге возможность либо получит окончательный и постоянный статус (с доработками или без), либо будет удалена.

Описание

Preview-возможность — это:

  • новая возможность языка Java («Preview-возможность языка»), или
  • новая возможность JVM («Preview-возможность VM»), или
  • новый связный набор модулей, пакетов, классов, интерфейсов, методов, конструкторов и полей в пространстве имён java.* или javax.* («Preview-API»)

дизайн, спецификация и реализация которой завершены, но которой пойдёт на пользу период широкой доступности и оценки, прежде чем она либо получит окончательный и постоянный статус в платформе Java SE, либо будет доработана или удалена.

Под «завершённостью» мы понимаем не «готовность на 100%», поскольку это означало бы, что отзывы бессмысленны. Мы имеем в виду, что Preview-возможность удовлетворяет двум критериям:

  1. (Готовность) Preview-возможность с высокой вероятностью будет готова на 100% в течение 12 месяцев. Этот срок отражает наш опыт: нормой являются два раунда Preview, т. е. Preview в Java $N и $N+1, затем окончательная версия в $N+2. Для API с исключительно большой поверхностью или глубоко взаимодействующих с JVM, а также для возможностей языка, которые по необходимости интегрируются с другими возможностями языка, мы предполагаем дополнительные раунды отзывов и доработок, поскольку такие возможности будут служить основой экосистемы Java на десятилетия вперёд.

  2. (Стабильность) Preview-возможность вполне могла бы получить окончательный и постоянный статус без каких-либо дальнейших изменений. Это подразумевает исключительно высокую степень уверенности в концепциях, лежащих в основе возможности, но не исключает полностью «поверхностных» изменений в ответ на отзывы. (Это особенно актуально для API, который неизбежно раскрывает концепции через бо́льшую поверхность, чем возможность языка; семантически стабильный API может пройти значительную синтаксическую шлифовку (например, переименование классов, добавление вспомогательных методов) в период Preview, прежде чем получит окончательный статус.)

Ключевые свойства Preview-возможности:

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

  2. Не экспериментальная. Preview-возможность не должна быть экспериментальной, рискованной, незавершённой или нестабильной. Экспериментальную возможность нельзя выпускать в статусе Preview в выпуске JDK типа Feature Release: её нужно дорабатывать и стабилизировать в собственном проекте, который, в свою очередь, выпускает двоичные файлы, явно отличающиеся от двоичных файлов проекта JDK. Для сравнения: если возможность HotSpot в статусе Experimental считается «готовой» на 25%, то Preview-возможность в Java SE должна быть «готова» как минимум на 95%. Ещё одно сравнение: уровень завершённости и стабильности, ожидаемый от Preview-API, значительно выше, чем уровень, ожидаемый от API в статусе Incubator.

  3. Всеобщая доступность. Umbrella JSR для платформы Java SE $N перечисляет Preview-возможности платформы. Потребность в отзывах и ожидания качества означают, что эти возможности не являются «необязательными»: все они должны полностью поддерживаться каждой реализацией Java SE $N. В частности, в реализации Java SE все Preview-возможности должны быть по умолчанию отключены, и реализация должна предоставлять механизм для включения всех Preview-возможностей. Реализация не должна позволять включать Preview-возможности по отдельности, поскольку все Preview-возможности имеют равный статус в платформе Java SE.

Общее правило: если возникает вопрос о каком-либо свойстве Preview-возможности и в этом JEP на него нет явного ответа, то подразумеваемый ответ — «Для Preview-возможности всё так же, как для окончательной и постоянной возможности».

Дизайн Preview-возможностей

Ограничений на форму Preview-возможности нет:

  • Preview-возможность языка может добавлять в синтаксис языка Java новые формы объявлений, инструкций, выражений и литералов; она может изменять статическую семантику (типизацию) существующих объявлений, инструкций, выражений и литералов; и она может изменять динамическую семантику (выполнение) существующих инструкций или выражений.

  • Preview-возможность VM может добавлять и изменять элементы формата файлов class; она может менять правила загрузки, компоновки и инициализации классов; и она может расширять и изменять набор инструкций JVM.

    (Preview-возможности VM отличаются от низкоуровневых возможностей реализации HotSpot JVM, которые настраиваются через java -X или java -XX. Preview-возможности VM также отличаются от возможностей HotSpot в статусе Experimental, которые являются ранними версиями низкоуровневых возможностей и должны явно разблокироваться в HotSpot во время выполнения. Например, когда Z Garbage Collector в HotSpot был в статусе Experimental в JDK 11, он включался через java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC.)

  • Preview-API может добавлять и изменять public-члены классов и интерфейсов в java.* и javax.*; добавлять и изменять public-классы и интерфейсы в java.* и javax.*; добавлять, изменять и экспортировать пакеты в пространствах имён java.* и javax.*; добавлять и изменять модули в пространстве имён java.*. Preview-API обычно находится в модуле java.base, но может находиться дополнительно или исключительно в других модулях java.*, включая модули, добавленные специально для этого Preview-API.

  • Preview-возможность может изменять текстовые спецификации существующих методов, конструкторов, полей, классов, интерфейсов, пакетов и модулей.

  • Платформа Java SE включает API не на Java, такие как JNI и JVM TI, и протоколы, не зависящие от языка, такие как JDWP и Java Object Serialization. API не на Java и протоколы, не зависящие от языка, могут расширяться для поддержки Preview-возможности. Такие расширения сами по себе считаются Preview-API. Например, функции JNI могут быть добавлены как Preview-API, позволяющий коду на C опрашивать артефакты времени выполнения, соответствующие Preview-API в java.*. Другой пример: протокол Java Object Serialization может быть изменён, чтобы особым образом обрабатывать объекты, классы которых используют Preview-возможности языка или VM.

  • Проектирование Preview-возможности платформы Java SE иногда может требовать развития широко известных служебных API, которые не входят в платформу Java SE, но тем не менее присутствуют в большинстве сред выполнения Java. Это в основном пакеты com.sun.*, экспортируемые модулями jdk.*. К ним относятся Compiler Tree API, HTTP Server API и Java Debug Interface. Эти «поддерживаемые» API com.sun могут расширяться для поддержки Preview-возможности. Такие расширения сами по себе считаются Preview-API. Например, в классы Java Debug Interface могут быть добавлены методы в качестве Preview-API для поддержки отладки Preview-возможности VM.

Preview-возможность не обязана принимать бросающуюся в глаза синтаксическую форму, чтобы подчеркнуть свой неокончательный статус. Напротив, Preview-возможность должна принимать сдержанную синтаксическую форму, предназначенную для её окончательной и постоянной версии. Например, Preview-возможность языка не должна нарушать соглашения, вводя ключевое слово в верхнем регистре, а Preview-API не должен добавлять временные пакеты в java.*. (В этом отличие от API в статусе Incubator, которые используют пространство имён jdk.incubator, чтобы подчеркнуть неокончательный статус своих пакетов и модулей.)

Размер Preview-возможности ничем не ограничен. Preview-возможность может быть любого размера, от очень большой до очень маленькой.

В качестве примера небольшой Preview-возможности языка рассмотрим изменение роли символа _ («подчёркивание»). Он был переопределён из идентификатора в ключевое слово, чтобы зарезервировать его для будущих возможностей языка. Чтобы дать существующему коду переходный период, переопределение было растянуто на два выпуска JDK: в JDK 8 использование _ в качестве идентификатора приводило к предупреждению javac, а в JDK 9 — к ошибке. В порядке, описанном в этом JEP, Java SE 8 специфицировала бы Preview-возможность языка, определяющую _ как ключевое слово. javac в JDK 8 выдавал бы предупреждение при использовании _ в качестве идентификатора, если только Preview-возможности не включены; в этом случае javac выдавал бы ошибку. Затем Java SE 9 специфицировала бы определение _ как ключевого слова на окончательной и постоянной основе, так что javac в JDK 9 всегда выдавал бы ошибку при использовании _ в качестве идентификатора, даже когда Preview-возможности не включены.

Спецификации Preview-возможностей

Preview-возможности языка и VM описываются в удобных для чтения документах, которые включены по ссылке в Java Language Specification (JLS, спецификация языка Java) и Java Virtual Machine Specification (JVMS, спецификация виртуальной машины Java).

Preview-API специфицируются вместе с постоянными API в API Specification платформы Java SE. Автор элемента Preview-API, будь то нечто большое, как модуль, или маленькое, как метод, должен зафиксировать его статус Preview, аннотировав его объявление с помощью @jdk.internal.javac.PreviewFeature. Эта аннотация заставляет инструмент javadoc добавить к элементу сообщение:

$ELEMENT_NAME is a preview API of the Java platform.
Programs can only use $ELEMENT_NAME when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.

Автор элемента Preview-API также должен указать тег @since в javadoc-комментарии элемента, обозначив выпуск, в котором @preview был впервые добавлен. Если элемент API в итоге становится окончательным и постоянным в Java SE $N, то тег @since нужно изменить так, чтобы он указывал выпуск $N, поскольку история элемента до $N не представляет долгосрочного интереса.

Иногда Preview-возможности необходимо или полезно влиять на спецификацию постоянного API, предметная область которого пересекается с Preview-возможностью. Например, поведение существующих методов постоянного API может немного отличаться, когда Preview-возможности включены. Чтобы описать, как Preview-возможность влияет на постоянный API, автор Preview-возможности должен использовать встроенный тег previewNote в javadoc элемента постоянного API:

{@previewNote $JEP_NUMBER [$SECTION_TITLE]}
... Preview-related text goes here ...
{@previewNote}

Если Preview-возможность затрагивает API не на Java или протокол, не зависящий от языка, автор должен обновить текстовую спецификацию этого API/протокола, чтобы было ясно, что некоторые элементы являются Preview-API. Обновление должно напоминать разработчикам, что «Preview-возможности могут быть удалены в будущем выпуске или стать постоянными возможностями платформы Java».

JLS и JVMS (включая включённые в них по ссылке спецификации Preview-возможностей языка и VM), а также API Specification и другие текстовые спецификации включаются по ссылке в спецификацию платформы Java SE посредством Umbrella JSR (обобщающего JSR) для платформы Java SE.

Umbrella JSR содержит исчерпывающий список элементов Preview-API в выпуске. Для удобства инструмент javadoc перечисляет все элементы Preview-API выпуска на странице «PREVIEW», аналогичной странице «DEPRECATED», на которой перечислены все устаревшие элементы API.

Java Compatibility Kit проверяет соответствие Preview-возможностей реализации соответствующему утверждению в JLS, JVMS или API Specification.

Связь Preview-возможностей языка/VM и Preview-API

Большинство Preview-API будут самостоятельными, не связанными с Preview-возможностями языка или VM. Например, рассмотрим HTTP Client API, который находился в статусе Incubator в Java SE 9 и 10: он мог бы выйти как самостоятельный Preview-API в Java SE 11, чтобы убедиться, что отзывы об API в статусе Incubator (jdk.incubator.http) по-прежнему применимы к полноценному API в Java SE (java.net.http).

Напротив, некоторые Preview-API будут разрабатываться совместно с Preview-возможностями языка или VM. Есть три вида совместно разрабатываемых Preview-API — необходимые, рефлексивные и вспомогательные. Они определяются так:

  1. Необходимый. Preview-API существует потому, что без него код не может воспользоваться Preview-возможностью языка или VM. Необходимый Preview-API находится в пакете java.lang или java.lang.annotation. JLS ссылается на необходимый Preview-API в нормативном тексте. Например, если бы улучшенный оператор for был Preview-возможностью языка, то java.lang.Iterable был бы его необходимым Preview-API.

  2. Рефлексивный. Preview-API существует, чтобы отразить Preview-возможность языка или VM в Core Reflection API, Method Handle API, Language Model API, Annotation Processing API, Compiler API или Compiler Tree API. Рефлексивный Preview-API находится в java.*, javax.* или com.sun.source.tree.*. JLS не будет ссылаться на рефлексивный Preview-API в нормативном тексте, но может ссылаться на него в ненормативном тексте. Как правило, рефлексивный Preview-API нужен, только когда Preview-возможность языка имеет форму объявления, а не оператора, выражения или литерала. Например, повторяемые типы аннотаций повлекли добавление методов в java.lang.Class (getAnnotationByType, getDeclaredAnnotationsByType).

  3. Вспомогательный. Preview-API — это набор полезных классов, интерфейсов и методов, которые способствуют использованию Preview-возможности языка или VM или облегчают его, но не являются для неё необходимыми. Маловероятно, что JLS будет как-либо ссылаться на вспомогательный Preview-API. Например, Streams API удобен разработчикам, использующим лямбда-выражения, но семантика лямбда-выражений не зависит от этого API.

Обычный Preview-API — это самостоятельный, необходимый или вспомогательный Preview-API. Обращение с кодом, использующим обычные Preview-API, сильно отличается от обращения с кодом, использующим рефлексивные Preview-API, как описано ниже в разделе «Использование Preview-возможностей».

Автор элемента рефлексивного Preview-API должен аннотировать его объявление с помощью @jdk.internal.javac.PreviewFeature(..., reflective=true). В этом случае инструмент javadoc добавляет к элементу другое сообщение, чем при reflective=false: (предложение «Programs can only use $ELEMENT_NAME when preview features are enabled.» опускается в соответствии с разделом «Особые правила использования рефлексивных Preview-API» ниже)

$ELEMENT_NAME is a reflective preview API of the Java platform.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.

В Java SE 14, до появления Preview-API, JLS перечисляла «необходимые» элементы API, которые были неизбежно связаны с Preview-возможностями языка и VM. Согласно JLS, использование таких элементов приводило к ошибке компиляции (или, соответственно, к предупреждению), если Preview-возможности были выключены (или, соответственно, включены). Этот порядок стал предшественником того, как JLS обращается с использованием самостоятельных, необходимых и вспомогательных Preview-API (теперь они перечисляются в спецификации платформы Java SE, а не в JLS).

Также до появления Preview-API автор элемента API, связанного с Preview-возможностью языка или VM, использовал тег @preview в javadoc-комментарии элемента, чтобы объяснить неокончательный статус элемента.

В самой ранней версии этого JEP предлагалось использовать механизм объявления устаревшим для пометки API, связанных с Preview-возможностями. Поэтому в Java SE 12 и 13 API, связанные с Preview-возможностями, с самого появления объявлялись устаревшими и подлежащими удалению, то есть при добавлении аннотировались с помощью @Deprecated(forRemoval=true, since=...). Например, в Java SE 13 были объявлены необходимый API, связанный с Text Blocks (текстовые блоки), и рефлексивный API, связанный с выражениями switch. Однако от подхода на основе объявления устаревшим в итоге отказались, поскольку сбивало с толку, что элемент API появлялся в том же выпуске Java SE, в котором объявлялся устаревшим, то есть на одном элементе API стояли и @since 13, и @Deprecated(forRemoval=true, since="13").

Связь Preview-возможностей языка/VM и API в статусе Incubator

Preview-возможность не должна зависеть от API в статусе Incubator, поскольку такой API не входит в платформу Java SE. Например, если бы Preview-возможность языка расширяла операторы throw и catch для поддержки третьего вида классов исключений (наряду с проверяемыми и непроверяемыми классами исключений), то корневому классу этого нового вида (аналогу java.lang.Exception и java.lang.RuntimeException) было бы неуместно находиться в API в статусе Incubator. Корневой класс должен быть Preview-API, разрабатываемым совместно с Preview-возможностью языка.

Preview-возможность может быть неформально связана с API в статусе Incubator, предлагаемым для удобства разработчиков, — возможно, как альтернатива описанной выше идее «вспомогательного» Preview-API. Например, если бы Preview-возможность языка вводила многострочные строковые литералы, её можно было бы представлять вместе с новыми методами обработки строк (например, для удаления переводов строк, изменения отступов и т. д.), которые находятся в статусе Incubator в пакете jdk.incubator.strings задолго до того, как появятся в статусе Preview в классе java.lang.String.

Реализация Preview-возможности языка в javac или реализация Preview-возможности VM в HotSpot может зависеть от API в статусе Incubator. Например, если бы javac реализовывал многострочные строковые литералы (Preview-возможность языка), сохраняя их в новом виде записей пула констант (Preview-возможность VM), то javac мог бы генерировать файлы class, байт-код которых получает многострочные строковые литералы, вызывая методы пакета jdk.incubator.strings.classfile (API в статусе Incubator). Для удобства разработчиков Incubator-модуль, содержащий этот пакет, разрешался бы автоматически, когда флаг --enable-preview передаётся javac или средству запуска java.

Preview-API в java.lang

Ответственный за возможность может захотеть выпустить API в пакете java.lang в статусе Preview. Preview-API может разрабатываться совместно с Preview-возможностью языка или VM (то есть быть «необходимым» или «вспомогательным» Preview-API, а может быть, и «рефлексивным» Preview-API), либо Preview-API может разрабатываться без какой-либо связи с Preview-возможностью языка или VM (то есть быть «самостоятельным» Preview-API). Например, в Java SE 14 Preview-API java.lang.Record разрабатывался совместно с Preview-возможностью языка (Records (записи)).

Добавление классов и интерфейсов в пакет java.lang — существенное изменение, потому что исходный файл на Java автоматически импортирует все классы и интерфейсы из java.lang через неявный import java.lang.*;. Смысл import java.lang.*; не зависит от того, включены или отключены Preview-возможности. Даже если Preview-возможности отключены, класс Record, выпущенный в статусе Preview в java.lang, импортируется каждым исходным файлом и может конфликтовать с уже существующими импортами класса или интерфейса Record из другого пакета. Чтобы увидеть, к чему приводит выпуск Record в статусе Preview в java.lang, сначала предположим, что есть пакет com.myapp, в котором объявлен класс Record, и тогда:

  • Любой исходный файл в пакете com.myapp по-прежнему будет компилироваться, потому что собственный класс Record этого пакета затеняет класс Record, импортированный из java.lang.

  • Любой исходный файл вне com.myapp, в котором написано import com.myapp.Record;, по-прежнему будет компилироваться. В таком исходном файле простое имя Record не является неоднозначным: класс Record, импортированный по полному имени, затеняет класс Record, импортированный из java.lang через импорт с подстановочным знаком (import java.lang.*;).

  • Исходный файл вне com.myapp, в котором написано import com.myapp.*;, по-прежнему будет компилироваться, если в нём не используется простое имя Record. Такое использование приводит к ошибке компиляции из-за неоднозначности: и Record из com.myapp, и Record из java.lang импортированы с подстановочным знаком, поэтому ни один из этих классов не затеняет другой.

Потенциальная несовместимость на уровне исходного кода из-за появления нового класса в java.lang досадна, но неизбежна, если язык Java должен развиваться. Поэтому ответственному за возможность следует выбирать наиболее выразительное простое имя, не слишком беспокоясь о возможном конфликте имён с классами и интерфейсами из других пакетов. Например, учитывая широкую популярность Records как Preview-возможности языка в Java SE 14, было бы неуместно называть совместно разработанный Preview-API, скажем, java.lang.JavaRecord или java.lang.Record2 лишь на случай, если в какой-нибудь библиотеке уже есть класс с именем Record. Появление java.lang.Record затронет только код, в котором используется импорт с подстановочным знаком (import com.myapp.*;), и такой код легко адаптировать с помощью конкретного импорта, чтобы получить библиотечную версию Record (import com.myapp.Record;).

Может показаться бестактным, что Preview-API ломает программу (вынуждая её использовать конкретный импорт из библиотеки), даже если ни программе, ни библиотеке этот Preview-API не нужен. Однако вина лежит на решении ответственного за возможность расширить java.lang; Preview-API лишь позволяет увидеть последствия этого решения раньше, а не позже.

Использование Preview-возможностей

Поскольку Preview-возможности ещё не получили окончательного и постоянного статуса в платформе Java SE, по умолчанию они недоступны во время компиляции и выполнения. Разработчики, которые хотят использовать в своих программах Preview-возможности языка и Preview-API, должны явно включить Preview-возможности в компиляторе Java и в среде выполнения. То есть разработчики должны дать согласие («opt in») дважды: один раз во время компиляции, когда исходный код на Java использует Preview-возможности языка и Preview-API, и ещё раз во время выполнения, когда исполняются соответствующие файлы class. Эти файлы class особые: их не следует распространять за пределы контроля разработчика, решившего использовать Preview-возможности.

(API, не относящиеся к Java, и/или независимые от языка протоколы платформы Java SE могут расширяться для поддержки Preview-возможности. Хотя такие расширения ещё не получили окончательного и постоянного статуса в платформе Java SE, они включены по умолчанию, поскольку требовать от разработчиков давать согласие («opt in») на их использование во время компиляции и выполнения непрактично. В этом отношении они похожи на рефлексивные Preview-API — API Java, которые предоставляют доступ к Preview-возможностям языка и VM.)

Во время компиляции работа разработчика выглядит так:

  1. При компиляции с отключёнными Preview-возможностями любая ссылка в исходном коде на (i) Preview-возможность языка, или (ii) класс или интерфейс, объявленный с использованием Preview-возможности языка, или (iii) обычный Preview-API (независимо от того, упоминается ли он по имени, вызывается или переопределяется) приводит к ошибке компиляции. Ошибка оправдана, потому что в одном из следующих выпусков языковая конструкция или элемент API могут быть удалены либо их поведение может несовместимо измениться. (Ссылки в исходном коде на рефлексивные Preview-API являются исключением, как описано ниже в разделе «Особые правила использования рефлексивных Preview-API».) Например, предположим, что класс LargeFile добавлен в java.io как обычный Preview-API. При отключённых Preview-возможностях import java.io.LargeFile; будет ошибкой компиляции; import java.io.*; ошибкой не будет, но ошибкой будет любое последующее использование простого имени типа LargeFile, например new LargeFile() или LargeFile.MAX_SIZE.

  2. При компиляции с включёнными Preview-возможностями любая ссылка в исходном коде на Preview-возможность языка вызывает предупреждение lint. Это предупреждение не предписано JLS, а характерно именно для javac. Его нельзя подавить с помощью @SuppressWarnings("...") или -Xlint:none.

  3. При компиляции с включёнными Preview-возможностями любая ссылка в исходном коде на (i) класс или интерфейс, объявленный с использованием Preview-возможности языка, или (ii) обычный Preview-API (независимо от того, упоминается ли он по имени, вызывается или переопределяется) вызывает предупреждение Preview. Это предупреждение предписано JLS, поэтому его выдают все компиляторы Java; оно не характерно только для javac. Его можно подавить с помощью @SuppressWarnings("preview").

По нашему мнению, важно напоминать разработчикам об использовании Preview-возможностей языка и обычных Preview-API в исходном коде, даже когда Preview-возможности включены во время компиляции. В конце концов, код может компилировать не тот разработчик, который его написал. Мы считаем, что подавляемого предупреждения достаточно, чтобы отметить использование обычного Preview-API (или класса или интерфейса, объявленного с использованием Preview-возможности языка), поскольку имена API, переданные в new или используемые в вызовах методов, легко заметить, даже если предупреждение подавлено. Напротив, мы считаем, что подавляемого предупреждения недостаточно, чтобы отметить использование Preview-возможности языка, поскольку языковые конструкции могут быть малозаметными и их можно упустить из виду, если предупреждение подавлено. Поскольку предупреждения, предписанные JLS, подавляются с помощью @SuppressWarnings("..."), они недостаточно сильны, чтобы отмечать использование Preview-возможности языка. Предупреждения, характерные для javac, могут быть сколь угодно сильными, поэтому в третьем случае выше используется предупреждение lint javac.

Чтобы среда выполнения могла определить, когда требуется согласие («opt in»), компилятор при компиляции с включёнными Preview-возможностями должен генерировать файлы class, в которых зафиксировано использование Preview-возможностей языка и Preview-API в исходном коде и/или Preview-возможностей VM в записях пула констант и атрибутах файла class. В частности:

  • Если исходный код на Java использует (i) Preview-возможность языка Java SE $N, или (ii) класс или интерфейс, объявленный с использованием Preview-возможности языка Java SE $N, или (iii) обычный Preview-API Java SE $N (независимо от того, упоминается ли он по имени, вызывается или переопределяется), то компилятор Java должен зафиксировать, что сгенерированный class-файл зависит от Preview-возможностей Java SE $N. Это относится и к случаю, когда Preview-возможность языка — это «синтаксический сахар», который можно скомпилировать, не полагаясь на Preview-возможности VM, такие как новые конструкции файла class.

  • Если компилятор Java или компилятор другого языка генерирует файл class, использующий Preview-возможность VM Java SE $N, то компилятор должен зафиксировать, что сгенерированный class-файл зависит от Preview-возможностей Java SE $N. Для компилятора Java это требование действует, даже если исходный код на Java не использует Preview-возможности языка, потому что компилятор Java может реализовать окончательную и постоянную возможность языка путём трансляции в Preview-возможность VM.

Файл class обозначает, что он зависит от Preview-возможностей Java SE $N, с помощью элемента major_version, соответствующего Java SE $N, и элемента minor_version, в котором установлены все 16 бит. Например, у файла class, зависящего от Preview-возможностей Java SE 17, будет версия 61.65535.

Использование minor_version предпочтительнее, чем access_flags, которое и так сильно загружено: если рассматривать объединение access_flags по структурам ClassFile, field_info и method_info, каждый бит отведён под какой-либо флаг. Неиспользуемые биты в любой отдельной структуре лучше оставить для реальных возможностей.

Использование minor_version также предпочтительнее таких конструкций, как пользовательский атрибут или специальная запись пула констант. Обработка пула констант в VM может зависеть от того, включены ли Preview-возможности, и такие конструкции были бы прочитаны слишком поздно.

Нарочито прямолинейный шаблон «все биты установлены» в minor_version выбран намеренно, вместо установки отдельного бита. Мы хотим избежать благонамеренных, но в конечном счёте ошибочных догадок о будущих ролях, а значит, и битовых шаблонах для minor_version. Мы также хотим избежать путаницы, связанной именно с младшим битом: если бы class-файл Java SE 17 с Preview-содержимым имел версию 61.1, её было бы легко ошибочно истолковать как «следующую» версию после 61.0.

Реализация JVM для Java SE $N не должна определять файл class, который зависит от Preview-возможностей другого выпуска Java SE, даже если в остальном эта реализация JVM понимала бы версию файла class. В частности, ClassLoader.defineClass должен завершаться неудачей для байтов файла class. Так такие файлы class не смогут запускаться годы спустя, когда Preview-возможности, которыми пользовался разработчик, давно будут удалены или окончательно утверждены в другом виде. По сути, Java SE $N+1 не заявляет обратной совместимости с Preview-возможностями Java SE $N.

Если Preview-возможности отключены во время компиляции, разработчикам не следует ожидать, что компилятор Java скомпилирует исходный код, который использует Preview-возможности языка или Preview-API любого выпуска Java SE, или скомпилирует исходный код с использованием файлов class, зависящих от Preview-возможностей VM любого выпуска Java SE. Аналогично, если Preview-возможности не включены во время выполнения, реализация JVM не загрузит class-файл, зависящий от Preview-возможностей любого выпуска Java SE.

Особые правила использования рефлексивных Preview-API. Чтобы фреймворки могли заранее поддержать Preview-возможность языка, им следует использовать рефлексивные Preview-API, разработанные вместе с ней. Так фреймворки не будут отставать, когда программы начнут писать с использованием постоянной версии этой возможности языка. Однако нежелательно, чтобы классы фреймворка помечались как зависящие от Preview-возможностей лишь потому, что фреймворк использует рефлексивные Preview-API. Поэтому правила использования рефлексивного Preview-API мягче, чем правила использования обычного Preview-API, приведённые в разделе «Использование Preview-возможностей». В частности:

  • Ссылка в исходном коде на элемент рефлексивного Preview-API разрешена всегда, даже когда Preview-возможности отключены. Такая ссылка в исходном коде вызывает подавляемое предупреждение о Preview, независимо от того, включены Preview-возможности или отключены. (Это то же предупреждение, которое определено выше для использования обычного Preview-API при включённых Preview-возможностях.)

  • Ссылка в исходном коде не приводит к тому, что сгенерированный файл class помечается как зависящий от Preview-возможностей Java SE $N. (Фактически для элемента рефлексивного Preview-API весь исходный код считается участвующим; правила об участвующем исходном коде см. в разделе «Особые правила для объявлений Preview-API».)

Во время компиляции Preview-API «скрыты», когда Preview-возможности отключены, поскольку доступ к ним контролирует компилятор Java. Во время выполнения, напротив, Preview-API не «скрыты», даже когда Preview-возможности отключены. Достаточно заинтересованная программа может с помощью рефлексии обнаруживать элементы Preview-API, создавать их экземпляры и вызывать их во время выполнения, независимо от того, включены Preview-возможности или отключены. Обращение к обычным Preview-API через рефлексию позволяет скомпилировать программу без включения Preview-возможностей и без того, чтобы её файлы class помечались как зависящие от Preview-возможностей. Это подразумевает, что программа рассчитана на запуск в нескольких выпусках Java — в одном выпуске, где Preview-API обнаруживается и используется через рефлексию, и в других выпусках, где он недоступен, — а это, в свою очередь, подразумевает, что программа рассчитана на распространение. Очень настоятельно рекомендуется не распространять программы, использующие обычные Preview-API. Особые правила использования рефлексивных Preview-API призваны поддержать фреймворки, которые правильно реагируют на присутствие Preview-возможностей языка в скомпилированном пользовательском коде (который, будучи пользовательским кодом, не распространяется).

Особые правила для объявлений Preview-API. Правила из раздела «Использование Preview-возможностей» применяются к исходному коду, который использует элементы Preview-API; они не применяются к исходному коду, который объявляет элементы Preview-API. Для исходного кода, который объявляет элементы Preview-API, действуют следующие правила:

  1. Исходный код, который прямо или косвенно является частью объявления Preview-API, называется участвующим исходным кодом. Участвующий исходный код можно компилировать независимо от того, включены Preview-возможности или отключены. Если Preview-возможности отключены (соответственно, включены), то там, где участвующий исходный код ссылается на собственные элементы своего Preview-API, ошибка компиляции (соответственно, предупреждение о Preview) не выдаётся.

    Например, если пакет java.io.fast вводится как Preview-API, то весь код в этом пакете будет «участвующим» и не будет вызывать ошибок/предупреждений при ссылках на собственные типы пакета. Аналогично, если класс java.io.LargeFile вводится как Preview-API, то весь код в классе — включая вложенные классы — будет участвующим и сможет свободно ссылаться на LargeFile, не вызывая ошибок/предупреждений.

    Другой пример: предположим, что sealed-интерфейс ClassFileElement в java.lang.classfile изменён так, чтобы разрешать новый подтип, представляющий новый вид атрибута ClassFile. Если бы новый подтип вводился как Preview-API, sealed-интерфейс был бы «участвующим» в объявлении этого подтипа и мог бы свободно ссылаться на подтип, не вызывая ошибок/предупреждений.

  2. Файлы class, сгенерированные для участвующего исходного кода, не помечаются как зависящие от Preview-возможностей Java SE $N. Согласно приведённым выше правилам, их использование другим кодом требует включения Preview-возможностей во время компиляции и выполнения; однако само их наличие во время компиляции или выполнения никак не влияет ни на какой код.

  3. Чтобы Preview-API можно было использовать внутри JDK, исходный код в том же модуле, что и Preview-API, считается участвующим; от него не требуется «явно соглашаться» во время компиляции и выполнения, чтобы использовать этот API без ошибок и предупреждений. Соответственно, когда Preview-возможности отключены, использование элемента Preview-API, экспортируемого модулем, приводит к ошибке компиляции только для кода в других модулях. (Это похоже на то, как использование элемента @Deprecated приводит к предупреждению только для кода, который сам не помечен как Deprecated (устаревший).)

Пример использования Preview-возможностей

Хотя этот JEP требует, чтобы Preview-возможности по умолчанию были отключены и чтобы существовал механизм их включения, конкретный механизм он не определяет. Поэтому для лучшего понимания в этом разделе описан возможный механизм включения Preview-возможностей в инструментах командной строки JDK: javac, java, javadoc, jshell и, возможно, jlink. Этот механизм — единственный флаг командной строки --enable-preview.

Время компиляции

javac в JDK $N принимает флаг --enable-preview только в сочетании с --release $N или -source $N. --enable-preview недопустимо, если операнд --release или -source не равен $N. (Для краткости в остальной части этого JEP упоминается только --release.)

Значение --enable-preview меняется от одного JDK к другому. Требование, чтобы разработчик явно указывал конкретный номер версии в --release, формирует ожидание, что код, полагающийся на Preview-возможности JDK $N, привязан к этому выпуску и может не компилироваться на JDK $N+1.

Сам --enable-preview не принимает номер версии, потому что его было бы легко неправильно истолковать. Например, в JDK 18 (гипотетический) флаг --enable-preview 19 создавал бы впечатление поддержки Preview-возможностей JDK 19, но на момент выпуска JDK 18 эти возможности ещё не известны.

В JDK 18:

javac Foo.java                                // Do not enable any preview features
javac --release 18 --enable-preview Foo.java  // Enable all preview features of Java SE 18
javac --release 17 --enable-preview Foo.java  // DISALLOWED

В JDK 19:

javac Foo.java                                // Do not enable any preview features
javac --release 19 --enable-preview Foo.java  // Enable all preview features of Java SE 19
javac --release 18 --enable-preview Foo.java  // DISALLOWED

Когда Preview-возможности Java SE $N включены (--release $N --enable-preview):

  • $N должен быть выпуском платформы Java SE, который реализует JDK, содержащий компилятор. Только компилятор в JDK $N может поддерживать Preview-возможности, определённые в Java SE $N. Нельзя ожидать, что компилятор в JDK $N+1 будет поддерживать Preview-возможности, определённые в Java SE $N, поскольку после Java SE $N они могли быть изменены или удалены. Разработчикам не следует ожидать, что javac в JDK $N скомпилирует исходный код, использующий Preview-возможности языка или Preview-API других выпусков Java SE, или скомпилирует исходный код с использованием файлов class, зависящих от Preview-возможностей VM (см. ниже) других выпусков Java SE.

  • Если ошибка компиляции возникает из-за неправильного использования Preview-возможности языка или любого Preview-API Java SE $N, то javac может указать, что ошибка связана именно с Preview-возможностью. Это помогает подчеркнуть, что сама возможность, как и связанные с ней ошибки компиляции, может измениться в будущих выпусках Java SE.

  • Если ошибки компиляции нет, то файл class, сгенерированный javac, зависит от Preview-возможностей Java SE $N, если исходный код, соответствующий файлу class, ссылается на (i) Preview-возможность языка Java SE $N, или (ii) класс или интерфейс, объявленный с использованием Preview-возможности языка Java SE $N, или (iii) обычный Preview-API Java SE $N (независимо от того, упоминается ли он по имени, вызывается или переопределяется). (Ссылки в исходном коде на рефлексивные Preview-API являются исключением, как описано выше в разделе «Особые правила использования рефлексивных Preview-API».)

  • Для любого исходного кода, который ссылается на класс или интерфейс, объявленный с использованием Preview-возможности языка Java SE $N, javac выдаёт подавляемое «предупреждение о Preview». javac выдаёт такое же предупреждение для любого исходного кода, который ссылается на обычный Preview-API.

Независимо от того, включены Preview-возможности или отключены, javac в JDK $N выводит сообщение, если обнаруживает в исходном коде использование Preview-возможности языка Java SE $N. Это сообщение нельзя отключить с помощью @SuppressWarnings в исходном коде, потому что разработчики должны всегда осознавать, что полагаются на версию Preview-возможности языка из Java SE $N; в Java SE $N+1 эта возможность может незаметно измениться. Сообщение выглядит так:

Note: Some input files use a preview language feature.
Note: Recompile with -Xlint:preview for details.

Отключённый по умолчанию анализ «preview», который предлагается в сообщении, даёт информацию о каждом использовании Preview-возможности языка Java SE $N. Кроме того, анализ может по мере возможности давать информацию о попытках использовать Preview-возможности языка других выпусков Java SE.

Использование элементов программы в статусе Deprecated приводит к тому, что javac выводит похожее сообщение с предложением Recompile with -Xlint:deprecation for details. Однако разработчики могут скрыть это сообщение с помощью @SuppressWarnings("deprecation") или @SuppressWarnings("removal"). Подавлять предупреждения об удалении, которые указывают на использование окончательно устаревших элементов, не рекомендуется, но это разрешено, поскольку последствия удаления таких элементов в будущем выпуске Java SE будут сразу и совершенно очевидны разработчикам (их программы перестанут перекомпилироваться). Preview-возможность языка, напротив, может быть изменена в будущем выпуске Java SE так, что совместимость на уровне исходного кода сохранится, а совместимость поведения — нет; программы, использовавшие эту возможность, успешно перекомпилируются, но будут иметь другой смысл. По сути, будущее Preview-возможности языка менее предсказуемо, чем будущее окончательно устаревшего элемента программы. Соответственно, риск использования Preview-возможности языка выше, чем риск использования окончательно устаревшего элемента программы, поэтому предупреждающее сообщение для Preview-возможности более жёсткое.

javadoc и jshell в JDK $N поддерживают Preview-возможности языка и API Java SE $N, только если при запуске указан --enable-preview.

При указании --enable-preview, если javadoc обрабатывает (i) элемент, объявленный с использованием Preview-возможности языка, или (ii) элемент, объявление которого ссылается на другой элемент, сам объявленный с использованием Preview-возможности языка, или (iii) элемент, объявление которого ссылается на Preview-API, в котором этот элемент не является участвующим (как описано в разделе «Особые правила для объявлений Preview-API»), то javadoc добавляет к элементу сообщение (с соответствующими изменениями):

$ELEMENT_NAME relies on preview features of the Java platform:
- $ELEMENT_NAME is declared using $FEATURE,
  a preview feature of the Java language.
- $ELEMENT_NAME refers to one or more preview APIs: ...
- $ELEMENT_NAME refers to one or more types which are declared
  using a preview feature of the Java language: ...

Programs can only use $ELEMENT_NAME when preview features are enabled.
Preview features may be removed in a future release,
or upgraded to permanent features of the Java platform.

Кроме того, если $ELEMENT_NAME — модуль, пакет, класс или интерфейс, то javadoc отмечает каждое использование элемента, ставя надстрочный индекс «PREVIEW» рядом с именем элемента везде, где на него ссылается сигнатура API.

javap в JDK $N не принимает флаг --enable-preview. Он автоматически выделяет места, где файл class зависит от Preview-возможностей Java SE $N. Для файла class, зависящего от Preview-возможностей VM более раннего выпуска Java SE, javap в JDK $N по мере возможности отображает Preview-содержимое.

Время выполнения

Лаунчер java в JDK $N принимает флаг --enable-preview для включения Preview-возможностей Java SE $N. Это позволяет выполнять любой файл class, зависящий от этих Preview-возможностей, — либо потому, что он напрямую полагается на Preview-возможности VM, либо потому, что он был скомпилирован из исходного кода, использовавшего Preview-возможности языка или Preview-API.

В JDK 18:

java Foo                            // Do not enable any preview features
java --enable-preview Foo           // Enable all preview features of Java SE 18
java --enable-preview -jar App.jar  // Enable all preview features of Java SE 18
java --enable-preview -m App        // Enable all preview features of Java SE 18

В JDK 19:

java Foo                            // Do not enable any preview features
java --enable-preview Foo           // Enable all preview features of Java SE 19
java --enable-preview -jar App.jar  // Enable all preview features of Java SE 19
java --enable-preview -m App        // Enable all preview features of Java SE 19

Когда Preview-возможности Java SE $N включены (--enable-preview):

  • $N должно быть выпуском платформы Java SE, которому соответствует реализация JVM. Только реализация JVM, соответствующая Java SE $N, может поддерживать Preview-возможности, определённые в Java SE $N. (Крайне маловероятно, что реализации JVM для Java SE $N+1 понадобится поддерживать Preview-возможности, определённые в Java SE $N.)

  • Если из-за использования в файле class Preview-возможности VM из Java SE $N при загрузке, связывании или выполнении возникает исключение, то реализация JVM может указать, что исключение вызвано Preview-возможностью VM из Java SE $N.

Независимо от того, включены Preview-возможности или нет, файлы class, которые не зависят от Preview-возможностей, загружаются, связываются и выполняются обычным образом.

Реализация JVM в JDK $N умеет показывать, какие загруженные классы зависят от Preview-возможностей Java SE $N. По умолчанию эта возможность выключена. Чтобы её включить, передайте -Xlog:class+preview программе запуска java.

Вопросы процесса

Проект JDK использует процесс JEP для управления разработкой возможностей. Каждая возможность языка Java, JVM и Java SE API начинает свою жизнь как JEP в статусе Draft (черновик), который проходит различные этапы выдвижения и одобрения, прежде чем будет интегрирован в Feature Release JDK.

(Под «возможностью» мы понимаем крупную часть новой функциональности или существенное расширение существующей функциональности; мы не имеем в виду небольшие улучшения или исправления при сопровождении. Статус Preview предназначен только для возможностей. Небольшие улучшения и исправления при сопровождении не следует выпускать в статусе Preview. Например, несколько новых методов в существующем классе не составляют Preview-API.)

Предполагается, что JEP может быть написан в черновом виде, рассмотрен, одобрен, по нему может начаться разработка, и он может быть подан как кандидат JDK (чтобы получить номер JEP) без оглядки на то, будет ли возможность интегрирована как Preview-возможность. Тем самым подтверждается, что для данного выпуска платформы Java SE техническое качество Preview-возможности равно качеству окончательной и постоянной возможности. Короче говоря, при проектировании и разработке возможности статус Preview не должен быть на первом месте.

В какой-то момент ответственный за JEP может проникнуться идеей сделать возможность доступной в статусе Preview. Ответственному следует обсудить в JEP последствия выпуска в статусе Preview, например факторы, из-за которых возможность может оказаться неудачной. Если JEP описывает Preview-возможность языка, которая разрабатывается совместно с Preview-API, то в JEP следует предостеречь разработчиков от использования Preview-API в контекстах, не связанных с Preview-возможностью языка. Ответственный за JEP также может обсуждать статус Preview применительно к реализации возможности (например, выбор имён пакетов для внутренних классов) и к связанным процессам (например, проверке совместимости).

Формальный шаг к доступности в статусе Preview происходит, когда JEP достигает статуса Proposed to Target (предложен для включения в выпуск). В этот момент ответственный за JEP должен указать, хочет ли он, чтобы возможность была доступна в статусе Preview в предлагаемом Feature Release JDK. Хороший способ сообщить о статусе Preview в JEP:

  1. В названии добавьте суффикс (Preview), (Second Preview) и т. д.
  2. В разделе «Аннотация» напишите This is a preview language feature / preview API in Java $N, например, см. JEP 359.
  3. В разделе «Описание» напомните читателю, что возможность находится в статусе Preview и её нужно включить. Лучше всего сделать это отдельным подразделом в начале раздела «Описание», например, см. JEP 476. Это также можно вплести в рассказ об основном примере, например, см. JEP 442.

Если JEP достигнет статуса Targeted (запланирован в выпуск), то Preview-возможность будет указана наряду с постоянными возможностями в Umbrella JSR платформы Java SE, например, см. Java SE 21. После того как JEP получил статус Targeted, его нельзя легко перевести из Preview в постоянные или наоборот; сначала он должен вернуться в статус Candidate (кандидат).

После того как код Preview-возможности интегрирован в JDK $N, JEP закрывается как обычно. Ответственному за JEP предстоит много работы по продвижению! Выбор способов и каналов для запроса и сбора отзывов разработчиков остаётся на усмотрение и опыт ответственного за JEP.

Ответственному за JEP следует помнить, что, хотя проект JDK выпускает релиз каждые шесть месяцев, это не значит, что между JDK $N и JDK $N+1 есть шесть месяцев на сбор и учёт отзывов. Из-за процесса выпуска между выходом JDK $N и началом JDK $N+1 (когда ветка jdkN+1 создаётся из ветки master репозитория jdk) проходит всего три месяца. В эти три месяца отзывы о Preview-возможности в JDK $N могут без опасений повлечь изменения в дизайне и реализации возможности для JDK $N+1. Однако после начала JDK $N+1 три месяца до выхода JDK $N+1 отведены на «стабилизацию»; это время нельзя использовать для перепроектирования или повторной реализации Preview-возможности. Поэтому ответственный за JEP, вероятно, захочет снова выпустить возможность в статусе Preview в JDK $N+1, чтобы собрать дополнительные отзывы, даже если возможность не изменилась по сравнению с JDK $N.

Чтобы снова выпустить возможность в статусе Preview, для JDK $N+1 нужно подать новый JEP. Хороший подход — написать в названии (Second Preview) и описать все изменения возможности в разделе «История», например, см. JEP 354. Новый JEP требуется, даже если Preview-возможность в JDK $N и JDK $N+1 ничем не отличается (такой сценарий встречается не так уж редко).

В конце концов ответственный за JEP должен решить окончательную судьбу Preview-возможности. Если принято решение удалить Preview-возможность, то ответственный должен подать задачу в JBS на удаление возможности в следующем Feature Release JDK; новый JEP не нужен. Если же принято решение сделать возможность окончательной, то ответственный должен подать новый JEP, указав доработки, сделанные по отзывам разработчиков. Названием этого JEP должно быть название возможности без прежнего суффикса (Preview) / (Second Preview) и без добавления нового суффикса, например (Standard) или (Final). В итоге этот JEP получит статус Targeted для следующего Feature Release JDK.

Альтернативы

При описанном выше подходе файл class может указывать, что зависит от Preview-возможностей Java SE $N, даже если в файле class на самом деле ничто от них не зависит. Обычно так бывает, если исходный код использует Preview-возможность языка, которая «исчезает при компиляции»; если бы не элемент minor_version со значением 65535, файл class можно было бы обычным образом выполнить в реализации JVM для Java SE $N. Альтернативный подход был бы менее строгим: требовать, чтобы файл class указывал на зависимость от Preview-возможностей Java SE $N, только если что-то в файле class действительно от них зависит. (Это можно было бы дополнить проверкой во время выполнения, что файлы class с minor_version, равным 65535, действительно используют Preview-возможности VM и/или Preview-API.) Однако файлы class, созданные при таком альтернативном подходе, могли бы широко распространяться и выполняться многие годы, ещё долго после того, как Preview-возможность языка изменена или удалена. Это нежелательно, потому что у разработчика нет стимула переписывать код, чтобы избавиться от Preview-возможности языка; он может считать её фактически окончательной и постоянной.

Проект JDK мог бы публиковать двоичные сборки Early Access (ранний доступ) (EA) во время стабилизации выпуска, до General Availability (общедоступный выпуск) (GA). Сборки EA могут обеспечить новым возможностям широкий охват, необходимый для получения полезных отзывов. Если бы доработки можно было спроектировать, специфицировать и реализовать до GA, то не было бы необходимости выпускать возможности в статусе Preview в выпуске GA. Однако исторически сборки EA не получили широкого распространения и не тестировались массово разработчиками; скорее всего, это будет ещё вернее, когда выпуски GA выходят каждые шесть месяцев.

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

  • когда возможность ещё не достигла технического качества, необходимого для включения в выпуск GA проекта JDK; или

  • когда целевая аудитория сборок EA невелика или сильно сосредоточена на проекте, а высокая заметность выпуска GA пока нежелательна; или

  • когда Preview-возможности так или иначе пронизывают всё и поэтому их трудно выделить в отдельно упакованные API, изолированные возможности языка или обособленные механизмы VM.

Другой способ включить отключённые возможности в выпуск GA — спрятать их за существующими флагами условного поведения. К ним относятся -Xlint:future в javac и -Xfuture в HotSpot. Кроме того, в исходном коде, который использует Preview-возможность языка, можно было бы ставить аннотацию на объявление класса или метода, указывающую, что javac должен автоматически включить Preview-возможность. (Одно из преимуществ такой локальной схемы, управляемой исходным кодом, в том, что аннотация могла бы указывать, в какой выпуск Java SE вошла Preview-возможность. Более поздние версии javac отказывались бы компилировать такой код, что позволило бы избежать проблемы, когда семантика возможности, но не синтаксис, изменилась после Preview и тем самым «незаметно» изменила бы смысл кода.)

Риски и допущения

При шестимесячном цикле возможности языка, VM или API, которая «опоздала на поезд», недолго ждать шанса успеть на следующий. Решение выпустить возможность в статусе Preview означает включение её в ветку стабилизации, так что на поезд, можно сказать, успели, но окончательная и постоянная возможность на самом деле не «в вагоне». Это может сбивать с толку. Хуже того, у ответственного за возможность, который рискует опоздать на поезд, может возникнуть соблазн пометить её как Preview, чтобы успеть.

Предполагается, что при шести месяцах между Feature Release JDK срока для отзывов о Preview-возможности достаточно для нетривиальной корректировки курса. Однако шести месяцев может оказаться слишком мало для того, чтобы разработчики тщательно изучили Preview-возможность. Особенно это верно с учётом того, что использование новых возможностей языка и API может потребовать существенного рефакторинга существующих программ. Кроме того, не все шесть месяцев перед следующим выпуском доступны для внесения изменений в выпуск. Поэтому предполагается, что ответственный за Preview-возможность в одном выпуске почти всегда будет снова выпускать её в статусе Preview в следующем выпуске. Чтобы проиллюстрировать это допущение: предположим, что возможность выходит в статусе Preview в JDK $N в марте, снова в статусе Preview в JDK $N+1 в сентябре и становится постоянной в JDK $N+2 в следующем марте; это даёт примерно восемь месяцев на отзывы (апрель–ноябрь), прежде чем процесс выпуска зафиксирует JDK $N+2.