Что современной Java нужно больше всего? Вот мой список желаний

Скоро Новый год, а значит время загадывать желания почти пришло. Я люблю язык Java, работаю с ним более 16 лет и у меня определенно есть пожелания по его улучшению.
Java — это хорошо, но у любого живого существа всегда есть место для улучшения, и Java — не исключение. С предварительным просмотром проекта Loom в JDK19 стало ясно, что есть еще много сумасшедших вещей, которые можно сделать даже в хорошо зарекомендовавшей себя экосистеме, которые заполняют пробелы и решают проблемы, иногда считающиеся неразрешимыми. Если вы не знаете, почему Loom неофициально называют убийцей асинхронности, просто посмотрите вот, это отличная вещь!
В этой статье я поделюсь своими личными пожеланиями по поводу языка Java помимо JEP, которые уже находятся в процессе.
«Ошибка на миллиард долларов»

Мое первое желание связано с печально известной проблемой обнуляемости.
Это старая боль Java и многих других языков, Ошибка на миллиард долларов. Как вы знаете, в Java значения всех переменных и полей, кроме примитивов, могут быть нулевыми или ненулевыми.
Для современности это немного устарело. Сегодня программисты хотят иметь больший контроль над тем, что может быть нулевым, а что нет. Многие современные языки предлагают синтаксические конструкции, такие как, например, язык Dart:
В этом примере someNullableString может быть нулевым или нет, что хорошо знакомо людям в мире Java.
Но someNonNullString никогда не будет нулевым, и компилятор гарантирует это. Не нужно ждать подвоха от someNonNullString, если вы можете получить к нему доступ где-то в своем коде — он никогда не будет нулевым. Очень кстати.
В Java, однако, вы не уверены ни в одной объектной переменной, и должны выполнять проверки ненулевого значения явно и повсеместно. Есть ряд решений этой проблемы, некоторые основаны на аннотациях и я не буду их сейчас обсуждать, т.к. для их работы нужны внешние инструменты (и честно говоря они мне не нравятся). Другой подход основан на использовании класса Optional, впервые появившегося в JDK 8.
На первый взгляд, Необязательный хорошо справляется со своей задачей — если вы обернете свой объект в Необязательный или получите Необязательный, возвращенный каким-либо методом, у вас будет довольно хороший API-интерфейс цепочки для работы с потенциально нулевым случаем. Проблема с опционом состоит из трех частей:
- Все переменные объекта Java являются необязательными, но не
Optional. Таким образом, вы должны явно создавать экземплярOptionalкаждый раз, когда вы хотите использовать его API, что является громоздким и в некоторых случаях увеличивает нагрузку на выделение новых объектов и сборщик мусора. - Все переменные объекта Java являются необязательными, поэтому, если вы имеете дело с объектом, отличным от
Optional, вы все равно не уверены, является ли он нулевым или нет. - Все переменные объекта Java являются необязательными, а
Optional— это просто еще один объект Java. Таким образом, даже если вы получите необязательный параметр, вы не уверены на 100%, что он сам по себе не нулевой!
Необязательный класс спасает нас в некоторых случаях, но он не решает проблему обнуляемости полностью, потому что не дает вам сильных гарантий.
Итак, каково решение?
Первое, что приходит на ум, если мы посмотрим на другие современные языки, — это улучшить систему типов, добавив «?» символ для обозначения типов, допускающих значение NULL, а те, у которых нет этого знака, никогда не должны быть нулевыми. Очевидно, что это неприемлемо для мира Java, так как нарушит обратную совместимость. Если вы сделаете это так, то весь старый код просто перестанет работать и его нужно будет переписывать, что неприемлемо. Обычно люди отказываются от этого шага и говорят: «Ну ладно, мы всегда будем жить с проблемой нулевого указателя».
Однако есть еще один вариант: вместо того, чтобы добавлять метки, допускающие значение NULL, к типам, почему бы не пометить типы, которые точно не являются нулевыми? Рассмотрим пример, если мы предполагаем, что ненулевой знак будет «!»:
Я уверен, что вы поняли идею. mainData равно nonNull, additionalData может быть нулевым. Возвращаемое значение будет nonNull, так как оно создается из ненулевых значений. Кроме того, в качестве щепотки синтаксического сахара было бы здорово дать хорошие возможности цепочки для классических нулевых значений, чтобы они напоминали необязательный API (но не обязательно создавать необязательный объект в процессе).
Добавление ненулевой маркировки полностью обратно совместимо. Никакие устаревшие библиотеки и API не находятся в опасности. Но в новом коде у вас будет гораздо более удобный синтаксис и гораздо более сильные гарантии того, что происходит, что, я думаю, имеет смысл.
Итак, мое первое желание — увидеть JEP, который решает проблему обнуляемости хотя бы для нового кода.
Ад параметризации

Java использует довольно консервативный способ объявления параметров для метода. Вы можете либо перечислить все параметры один за другим, либо добавить varargs, чтобы собрать произвольное количество параметров в массив. На первый взгляд достаточно, давайте рассмотрим пример:
У нас есть интерфейс с одним методом, который принимает три параметра. Хороший. Но что, если мы хотим, чтобы один параметр был необязательным, а в случае его отсутствия мы хотим, чтобы использовалось значение по умолчанию? Допустим, b должно быть необязательным со значением по умолчанию, если оно не установлено:
Выглядит просто, верно?
Теперь давайте предположим, что все три параметра являются необязательными и должны иметь некоторые значения по умолчанию, если они не установлены. Сколько тогда нам понадобится методов, чтобы справиться со всеми комбинациями? 2³ = 8. С большим количеством необязательных параметров вам потребуется экспоненциально больше комбинаций методов для моделирования. Упс, добро пожаловать в ад параметризации…
В некоторых областях ад параметризации поражает вас больше всего. Например, когда вы работаете с пользовательским интерфейсом, обычно вы можете изменить множество параметров, как визуальных, так и поведенческих: различные отступы/поля/интервалы, цвета, свойства макета, шрифты, границы и т. д. Некоторые из них вы можете изменить в конкретном случае. ситуации, но в большинстве случаев вы, вероятно, будете использовать значения по умолчанию.
Давайте посмотрим на старый добрый пример Swing (вы, наверное, думаете, что Swing немного устарел, что, безусловно, верно, но одна из лучших Java IDE до сих пор использует его под капотом). Есть класс JDialog, отвечающий за… ну диалоги. Когда вы пытаетесь создать объект этого класса, вам нужно будет выбрать конструктор:

Если быть точным, вам придется выбрать один из 16 конструкторов. Такое число отражает простую вещь: некоторые параметры, такие как заголовок или модальность, являются необязательными в контексте создания JDialog, и попытка соблюсти их приводит к комбинаторному взрыву числа доступных конструкторов.
Теперь давайте на мгновение рассмотрим фреймворк Flutter. Flutter — это фреймворк пользовательского интерфейса, основанный на языке Dart. У него те же проблемы, что и у Swing или любого другого UI-фреймворка: большое количество необязательных параметров с предопределенными значениями по умолчанию. Давайте посмотрим на конструктор класса Flutter MaterialApp:

Конструктор имеет 35 параметров, и каждый из них является необязательным. В Dart параметры в {} считаются необязательными, но вы также можете добавить классические обязательные параметры, просто поместив их вне {}.
Не сосредотачивайтесь сейчас на ключевом слове this, это синтаксис для автоматического присвоения значений полям класса, что, безусловно, круто, но пока мы придерживаемся темы необязательных параметров. Для некоторых параметров используются значения по умолчанию, а для параметров без назначения по умолчанию используются значения NULL. Вот пример того, как вы можете использовать этот конструктор:

Бинго! Вы установили только несколько необходимых параметров и оставили остальные использовать значения по умолчанию. Для достижения такого удобства вам придется создать 2³⁵ метода в классе MaterialApp, используя старый добрый комбинаторный подход! Пожалуйста, не пробуйте, иначе вы, скорее всего, опоздаете на рождественский ужин.
Как избежать такого ада параметризации в Java? Наиболее широко используемым решением является шаблон проектирования Builder, что означает, что мы должны использовать другой объект с полями, которые ссылаются на необязательные параметры метода, который мы хотим вызвать, установить его значения, объединив методы установки Builder в цепочку, и в конце вызвать метод 'build', чтобы, наконец, сделать то, что нам нужно. Звучит немного громоздко. Построители могут быть удобны в сложных сценариях сборки, когда создание объекта не является простым, например, если процесс создания разбит на несколько шагов с различными доступными параметрами на каждом этапе. Однако для простых случаев, если у вас удобный синтаксис необязательных параметров, шаблон построителя больше не нужен.
Есть хорошие новости. Даже определение необязательных параметров в стиле Dart с {} и их использование с двоеточиями, скорее всего, не нарушит обратную совместимость, если оно будет сразу добавлено в язык Java. Конечно, могут быть грубые углы, но в принципе проблем нет. И помимо синтаксиса Dart, может быть много альтернатив, которые, вероятно, могли бы сделать то же самое лучше.
Итак, мое второе желание — увидеть необязательные параметры метода в одном из будущих JEP.

Спасибо за прочтение! Есть ли у вас какие-либо пожелания по поводу Java, которые нужно поместить в письмо Деду Морозу? Дайте мне знать. Спасибо за прочтение.
Подписывайтесь на мой Telegram-канал, я еженедельно пишу там пару слов о состоянии экосистемы Java.