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

JEP draft: Preview Features: A Look Back, and A Look Ahead

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

ОтветственныйAlex Buckley
ТипInformational
ОбластьSE
СтатусDraft
Обсуждениеjdk dash dev at openjdk dot java dot net
ТрудоёмкостьS
ДлительностьS
Связан сJEP 12: Preview Features
Создан2023/01/18 21:56
Обновлён2023/11/18 15:13
Задача8300604

Прошло более четырёх лет с тех пор, как мы ввели понятие Preview-возможностей в JEP 12, так что сейчас хороший момент, чтобы оценить, как они себя показали, и понять, нужно ли что-то подправить в дальнейшем.

Preview-возможности языка

В языке Java процесс Preview хорошо справился со своей главной задачей: добиться того, чтобы возможности были «сделаны правильно» до того, как они станут окончательной и постоянной частью языка. Как? За счёт подходящего промежутка времени, в течение которого возможности можно улучшить на основе отзывов пользователей. Switch-выражения, Text Blocks (текстовые блоки), Records (записи), Sealed Classes (запечатанные классы) и Pattern Matching (сопоставление с образцом) для instanceof успешно прошли статус Preview и стали высоко ценимыми постоянными возможностями языка.

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

Preview-возможности языка идеально сочетаются с шестимесячным циклом выпусков JDK. Десять лет назад крупный проект по развитию языка Java не мог выйти, пока не были проработаны все его возможности и не была доведена до конца общая модель программирования. Вспомним, как Project Lambda ввёл лямбда-выражения + ссылки на методы + локальный вывод типов + методы по умолчанию + потоки данных (streams): даже при заделе, созданном в JDK 7, на создание JDK 8 ушло три года сосредоточенной работы. Сегодня проект вроде Amber может выпускать каскад Preview-возможностей в последовательных выпусках JDK (16, 17, 18, 19...), и в итоге наступает выпуск, в котором в статусе Preview представлена вся новая модель программирования целиком. Например, Pattern Matching начался в JDK 14 с шаблонов типов в instanceof, а к JDK 19 превратился в узнаваемый стиль программирования, охватывающий instanceof, switch, Records и запечатывание.

Один из наших руководящих принципов состоит в том, что мы включаем Preview-возможность в JDK, только если она действительно движется к окончательному и постоянному статусу в разумные сроки. Если похоже, что путь возможности сорвётся — по качеству или по срокам, — мы должны быть готовы отказаться от неё после того, как она побывала в статусе Preview, и до того, как она станет постоянной. К счастью, нам пока не приходилось этого делать.

(Некоторые ошибочно считают, что от «Raw String Literals» отказались после статуса Preview в JDK 12, но на самом деле эта возможность так и не вышла в JDK 12: мы сменили направление и вместо неё выпустили в статусе Preview «Text Blocks» в JDK 13.)

Хотя небольшие возможности языка успешно проходили короткий период Preview, прежде чем стать постоянными, мы поняли, что крупным возможностям языка может понадобиться больше двух версий Preview, особенно если они взаимодействуют с другими возможностями языка, которые сами находятся в статусе Preview. Например, Pattern Matching для switch дважды выходил в статусе Preview сам по себе, а затем ещё дважды из-за взаимодействия с другой, более новой Preview-возможностью (Record Patterns (шаблоны записей)). Три и более версии Preview отражают стремление к тому, чтобы все возможности языка слаженно работали вместе, и это несравнимо лучше, чем пытаться «придержать поезд», пока все связанные возможности не будут в равной степени готовы к статусу Preview.

В отличие от улучшений реализации HotSpot JVM — например, для сборки мусора, — JEP 12 предусматривал выпуск в статусе Preview изменений в структуре и связывании файлов class и в наборе инструкций абстрактной виртуальной машины Java. Возможно, это удивительно, но до сих пор не было ни одной Preview-возможности VM, за исключением Sealed Classes (формально это Preview-возможность языка, но она требует поддержки со стороны виртуальной машины Java). Мы можем ожидать Preview-возможности VM от Project Valhalla в ближайшие несколько лет (например, primitive classes).

Preview-API

С самого начала мы понимали, что Preview-возможности языка часто будут разрабатываться вместе с элементами API, в первую очередь для поддержки рефлексии. Например, record-классы вышли в статусе Preview вместе с Class::getRecordComponents, а sealed-классы — вместе с Class::getPermittedSubclasses.

Затем мы поняли, что выпускать API в статусе Preview сами по себе тоже ценно. Первые «самостоятельные» Preview-API появились в JDK 19: API для Virtual Threads (виртуальные потоки) и Foreign Function & Memory (FFM) API. Общие для API и возможностей языка терминология и механизмы Preview (например, --enable-preview) хорошо себя показали.

Тем не менее API и возможности языка различаются в важном отношении. У возможности языка небольшая внешняя поверхность, но широкая концептуальная область: вспомните, как синтаксис лямбд -> открывает путь в мир функционального программирования. API же имеет большую внешнюю поверхность, которая более или менее и определяет концептуальную область: «карта и есть территория». Для возможности языка обычное дело — изменить поведение в граничных случаях так, что это не затрагивает синтаксис и мало заметно программистам, но изменение почти любого аспекта API сразу и широко заметно. Оно затрагивает как программистов, которые вызывают API напрямую из приложений, так и программистов, которые строят библиотеки поверх API.

В JEP 12 мы прямо сказали, что выпуск API в статусе Preview, скорее всего, повлечёт более заметные изменения, чем выпуск в статусе Preview возможности языка: «семантически стабильный Preview-API всё ещё может пройти значительную синтаксическую доработку (например, переименование классов, добавление вспомогательных методов) на пути к окончательному статусу».

В качестве примера рассмотрим FFM API. Он призван решить сложную задачу — обеспечить безопасный доступ к памяти, выделяемой и освобождаемой другими сторонами, — поэтому в ходе его разработки исследовались различные формы API для ограничения и защиты доступа к памяти. Его основные понятия стабильны (сегменты, аллокаторы, компоновщики и т. д.), но их выражение в конкретных элементах API менялось в ответ на отзывы пользователей. В свою очередь, библиотекам, построенным поверх FFM API в период Preview, приходилось подстраиваться под его меняющиеся элементы API.

Новый подход к Preview-API

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

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

Мы сохраним ориентир, согласно которому в общем случае Preview-возможность должна быть завершена на 100 % в течение 12 месяцев. Однако этот ориентир, подразумевающий, что Preview-возможность появится в одном или двух выпусках JDK, прежде чем стать окончательной, вероятно, слишком амбициозен для API, которые охватывают сложные понятия, имеют исключительно большую внешнюю поверхность или глубоко взаимодействуют с VM. Для таких API следует ожидать дополнительных циклов сбора отзывов и доработки, поскольку они будут служить основой экосистемы Java ещё многие десятилетия.

(Мы рассматривали вариант сделать ставку на API в статусе Incubator (инкубационный модуль) как на модель для API, которые концептуально завершены, но подвержены синтаксическому дрейфу. Однако Incubator-API нестандартны и поставляются в отдельных Incubator-модулях. Из-за нестандартности стандартные пакеты java.* не могут ссылаться на них в сигнатурах классов и членов, а поставка вне java.base делает реализацию низкоуровневого API, такого как Virtual Threads, чрезвычайно сложной. Кроме того, концептуальные и практические различия между статусами Incubator и Preview большинству разработчиков непонятны. Предлагая высококачественные API со стабильным ядром под привычной меткой «Preview», мы получим гораздо больше отзывов, чем если будем втискивать их в Incubator-модули, которые привлекают мало внимания. Тем не менее мы продолжим использовать статус Incubator для API, уровень полноты и стабильности которых значительно ниже, чем у Preview-API.)