Все плохое в Java хорошо для вас — почему нулевые значения и проверенные исключения — ваши друзья

Все плохое в Java хорошо для вас — почему нулевые значения и проверенные исключения — ваши друзья

14 июня 2023 г.

Все плохое — это хорошо для вас — это книга о поп-культуре, в которой говорится, что некоторые вещи, которые мы считаем плохими (например, ТВ) имеют огромные преимущества для нашего благополучия. Мне нравится принцип разрушения общепринятого повествования, и я постоянно вспоминал об этом, обсуждая некоторые из наиболее спорных особенностей и проблем в Java. Это фича, а не баг…

Одна из моих любимых вещей в Java — это ее тенденция двигаться медленно и обдуманно. Он не дает нам сразу то, что мы хотим. Команда Java понимает требования и рассматривает другие реализации, а затем учится на них.

Я бы сказал, что движущая философия Java заключается в том, что ранняя пташка проглатывается змеей.

https://www.youtube.com/watch?v=cWdkMfmpsps&embedable=true

Проверенные исключения

Одна из наиболее ненавидимых всеми функций Java — проверяемые исключения. Насколько я помню, это единственная инновационная функция, представленная в Java. Большинство других концепций Java существовали в других языках, проверенные исключения — это совершенно новая идея, отвергнутая другими языками. Они не являются «забавной» функцией, я понимаю, почему людям они не нравятся. Но это потрясающий инструмент.

Самая большая проблема с проверенными исключениями заключается в том, что они плохо вписываются в функциональный синтаксис. Это справедливо и для обнуляемости (которую я вскоре обсужу). Это справедливая жалоба. Поддержка функционального программирования была привязана к Java, и с точки зрения обработки исключений она была сделана плохо. Компилятор Java мог обнаружить проверенные исключения и потребовать обратного вызова ошибки. Это было ошибкой, когда эти возможности были представлены в Java 8. Например. если бы эти API были лучше представлены в Java, мы могли бы написать такой код:

Компилятор может заставить нас написать обратный вызов errorHandler, если он отсутствует, что полностью удовлетворит дух проверяемых исключений. Это возможно, потому что проверяемые исключения являются функцией компилятора, а не JVM. Компилятор может обнаружить проверенное исключение в лямбда-выражении и потребовать специально аннотированного обратного вызова для обработки исключений.

Почему не было добавлено что-то подобное?

Это, вероятно, из-за общей неприязни к проверенным исключениям. Никто не пытался придумать альтернативу. Никому они не нравятся, потому что никому не нравится надоедливая особенность, заставляющая убирать за собой. Мы просто хотим писать код, проверенные исключения заставляют нас нести ответственность, даже когда мы просто хотим написать простой привет, мир…

Это в значительной степени ошибка… Мы можем объявить, что main выдает исключение и создать простой hello world без обработки проверенных исключений. В больших средах приложений, таких как Spring, проверенное SQLException обернуто версией RuntimeException того же класса. Вы можете подумать, что я против этого, но это не так. Это прекрасный пример того, как мы можем использовать проверенные исключения для очистки постфактум. Очистка выполняется внутри Spring, на этом этапе логика обработки исключений больше не имеет решающего значения и может быть преобразована в исключение времени выполнения.

Я думаю, что большая часть ненависти к API происходит из-за плохих версий этого исключения, таких как MalformedURLException или исключений кодирования. Эти исключения часто вызываются для постоянного ввода, который никогда не должен давать сбоев. Это просто лишнее и плохое использование языковых возможностей. Проверенные исключения должны создаваться только тогда, когда мы можем сделать очистку. Это проблема API, а не языковой функции.

Нет

Выплескивать ненависть на null было в тренде последние 15 с лишним лет. Да, я знаю эту цитату. Я думаю, что люди злоупотребляют им.

Нуль — это факт сегодняшней жизни, нравится вам это или нет. Он присущ всему: базам данных, протоколам, форматам и т. д. Null — это глубокая часть программирования, и в обозримом будущем он никуда не исчезнет.

Спорить о нуле бессмысленно. Спор, который имеет значение, заключается в том, лучше ли лекарство, чем болезнь, и я еще не убежден. Важно не то, что null было ошибкой, а то, что мы делаем сейчас.

Честно говоря, это напрямую связано с вашей любовью к парадигмам функционального программирования. Null плохо работает в FP, поэтому он стал боксерской грушей для парней из FP. Но делаем ли мы шаг назад или шагаем вперед?

Давайте разобьем это на три отдельных обсуждения:

  • Производительность
  • Ошибки
  • Простота программирования

Производительность

Null работает быстро. Сверх быстрый. Буквально бесплатно. ЦП выполняет для нас проверку на нулевое значение и обрабатывает исключения как прерывания. Нам не нужно писать код для обработки null. Альтернативы могут иметь очень низкие накладные расходы и иногда могут преобразовываться в нуль для повышения производительности ЦП. Но это сложнее настроить.

Утечка абстракций и нуль — это то, как работает наше оборудование. Для большинства намерений и целей это лучше.

Есть предостережение. Нам нужна возможность помечать некоторые объекты как ненулевые для лучшего распределения памяти (как и планирует сделать Valhalla). Это позволит улучшить расположение памяти и может помочь ускорить код. Обратите внимание, что мы можем добиться этого, сохраняя семантику объекта, маркера будет достаточно.

Я бы сказал, что в этом раунде побеждает ноль.

Ошибки

Люди ненавидят исключение NullPointerException. Это сбивает меня с толку.

NullPointerException — одна из лучших ошибок. Это принцип отказоустойчивости. Ошибка обычно проста для понимания, и даже если это не так; это не за горами. Эту ошибку легко исправить. Альтернативой может быть инициализация пустого объекта, который нам нужно проверить, или установка фиктивного объекта для представления null.

Откройте базу данных, которая существует достаточно давно, и найдите «undefined». Бьюсь об заклад, в нем довольно много записей… Это проблема с ненулевыми значениями. Вы можете не получить отказ сразу. Вы получите нечто гораздо худшее. Скрытая ошибка, которая ползает по системе и загрязняет ваши данные.

Поскольку null настолько прост и его легко обнаружить, существует множество инструментов, которые могут работать с ним как во время выполнения, так и во время разработки. Когда люди упоминают об исключении нулевого указателя в рабочей среде, я обычно спрашиваю: а что могло быть альтернативой?

Если бы вы могли инициализировать значение для начала, то почему вы этого не сделали?

В Java есть ключевое слово final, вы можете использовать его, чтобы сохранить ненулевые значения с отслеживанием состояния. Изменяемые значения являются основной причиной неинициализированных или нулевых значений. Вполне возможно, что язык, отличный от null, не подведет. Но будет ли его результат хуже?

По моему опыту, поврежденные данные в хранилище гораздо хуже. Проблема коварна и прячется под поверхностью. Нет никакой подсказки относительно происхождения проблемы, и нам нужно установить «ловушки», чтобы отследить ее. Дай мне отказоустойчивость в любой день.

На мой взгляд, у null это бесспорно…

Простота программирования

Важно понимать, что null является требованием современных вычислений. Вся наша экосистема построена на нулевом уровне. Такие языки, как Kotlin, отлично демонстрируют это, у них есть нулевые и ненулевые объекты.

Это означает, что у нас есть дублирование. Каждое понятие, связанное с объектами, выражается дважды, и нам нужно поддерживать семантику между нулевым и ненулевым значением. Это поднимает планку сложности для разработчиков, плохо знакомых с такими языками, и создает странный синтаксис.

Это само по себе было бы хорошо, если бы сложность окупилась. К сожалению, такие функции разрешают только самые тривиальные непроблемные случаи null. Сложные объекты не поддерживаются, так как они содержат значение NULL, полученное из внешних источников. Мы усложняем язык ради ограниченной выгоды.

Шаблон

Раньше это было более серьезной проблемой, но при сравнении типичного файла Java с TypeScript или JavaScript разница не так велика. Все-таки люди придираются. Умный инженер, которого я знаю в Интернете, назвал использование точек с запятой в языках «ленью».

Я этого не понимаю. Мне нравится требование точки с запятой, и меня всегда сбивают с толку люди, у которых с этим проблемы. Как автор, это позволяет мне форматировать мой код, игнорируя длину строки. Я могу разрывать строку в любом месте, точка с запятой имеет значение. Во всяком случае, я бы с удовольствием отказался от возможности писать условные операторы без фигурных скобок, например:

if(..) x();
else y();

Это ужасно. Я блокирую их в своих требованиях к стилю; это верный путь к катастрофе с неясным началом и концом.

Организация сил Java, это замечательная вещь. Классы должны находиться в определенном файле, а пакеты сопоставляться с каталогами. Это может не иметь значения, когда ваш проект крошечный, но когда вы работаете с огромной кодовой базой, это становится находкой. Вы бы сразу знали, где искать подсказки. Это мощный инструмент. Тем не менее, это приводит к некоторой многословности и некоторым глубоким структурам каталогов. Но Java был разработан людьми, которые создают проекты 1M LoC, он хорошо масштабируется благодаря шаблону. Мы не можем сказать то же самое о некоторых других языках.

Быстро двигаться

Многие вещи в Java не очень хороши, особенно при создании более смелых стартап-проектов. Вот почему я так в восторге от Manifold. Я думаю, что это способ добавить в Java все «крутые штуки», которые нам нужны, сохранив при этом производительность, совместимость и стабильность, которые нам так нравятся.

Это может позволить сообществу двигаться вперед быстрее и экспериментировать, в то время как Java как платформа может идти медленным и стабильным путем.

Заключительное слово

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

Тем не менее, контрапункты формочек для печенья не сокращают его. Факты не дают ясной картины в их пользу. Всегда есть компромисс, и Java шла по уникальному канату. Даже незначительное движение в неправильном направлении может привести к быстрому падению. Тем не менее, он сохраняет свою привлекательность, несмотря на попытки нескольких различных групп представить его как устаревший. Это привело к нелепому восприятию разработчиков Python и JavaScript как «более новых» языков.

Я думаю, что решение для этого двоякое. Нам необходимо рассказать о преимуществах подхода Java к этим решениям. Нам также нужны такие решения, как Manifold, чтобы свободно исследовать потенциальные направления. Без обременения JCP. Наличие рабочего доказательства концепции значительно облегчит интеграцию новых идей в Java.

:::информация Также опубликовано здесь.

:::


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE