Обработка значений null
часто считается слабым местом в Java. Для этого есть несколько причин.
Наиболее часто упоминаемая проблема – известное NullPointerException
, хотя нет четкого обоснования, почему это является проблемой. В конце концов, это всего лишь признак проблемы, но не сама проблема.
Реальная проблема гораздо глубже. Те, у кого есть опыт написания кода на C или C++, знают этот вопрос гораздо лучше. При написании кода на C/C++ инженер всегда должен иметь в виду бесчисленные нюансы, связанные с безопасностью доступа, неопределенным поведением, выделением памяти, копированием/перемещением объектов и так далее и тому подобное. Все эти многочисленные детали создают постоянное “давление”, потребляют драгоценные ресурсы мозга инженера и приводят к замедлению скорости разработки. Даже закоренелые сторонники C/C++ не могут отрицать этот факт.
Аналогичная проблема существует с null
значениями в Java.
Java часто считается многословным языком. Это правда. Это обратная сторона его очевидности. Почти каждое намерение в Java может быть (и обычно есть) явно выражено в коде. Добавляя некоторый дополнительный текст, мы, в свою очередь, получаем сразу доступный контекст, который не нужно держать в голове при чтении Java-кода. Значения null
нарушают это правило. Они не проявляются в коде.
Значения null
могут появляться по разным причинам. Это может быть инициализация по умолчанию, значение, возвращаемое из какого-либо метода, или даже явно присвоенное значение null
. Имея в виду, что иногда null Может появиться
, проверка документации метода или кода на возможность возврата такого значения – все это создает “давление”, очень похожее на описанное выше для C/C++.
Что можно сделать для решения этой проблемы? Как устранить это постоянное “давление”, которое влияет на нашу производительность?
Конечно, есть несколько способов решить эту проблему. Мы можем:
- Добавить
@NotNull
( или аналогичные) аннотации. Не делает, по сути, ничего, кроме добавления некоторого (ложного) ощущения, чтозначения null
каким-то образом обрабатываются. - Установите строгие правила и проверяйте каждое значение, которое потенциально может быть
null
. Это рабочее решение, но за счет загрязнения кода бесчисленными проверкамиnull
. И, что ж, никто не идеален, мы можем совершать ошибки, и компилятор нам не поможет. - Придумайте свой собственный язык. Авторы Kotlin сделали именно это. Опять же, это рабочее решение, но, как по мне, это перебор. Тем не менее, у него есть одна важная вещь: типы с нулевым и ненулевым значением различны и четко выражены в коде.
- Используйте подходы к функциональному программированию –
Может быть,
монада в какой-то форме.
Последний пункт – это то, что я собираюсь обсудить более подробно.
Необязательно: хороший, плохой, уродливый
Введено в Java 8 Необязательный класс
предназначен для обработки отсутствующих значений вместо null
. В какой-то степени это Java-реализация Возможно,
монада (но не полностью, см. Ниже).
В то время как некоторые могут рассматривать это как просто причудливый способ работы со значениями null
, на самом деле у него есть гораздо более важное преимущество: явное выражение намерения. Как только вы обернете поле, параметр или переменную в Необязательно
вы сразу же делаете свое намерение явным и понятным для читателя. Другими словами – вы добавляете этот недостающий контекст. В то же время компилятор начинает видеть разницу, вы больше не одиноки.
Итак, используя Optional
инженер убивает трех зайцев одним выстрелом:
- Делает потенциально отсутствующее значение явным в коде и устраняет “давление” со стороны читателя.
- Позволяет компилятору контролировать доступ к значению.
- Делает обработку значений
null
удобной.
К сожалению Необязательный
имеет свои собственные проблемы.
Как упоминалось выше, Необязательная
“в некоторой степени” реализация Может быть,
монада. Он ссылается на императивный мир и имеет внешне доступную ценность. Вызвав метод get()
, вы можете попытаться получить его. Но если вы это сделаете, это может вызвать исключение NullPointerException
. Что противоречит целевому назначению Необязательного
– be безопасного контейнера для потенциального null
(т.е. отсутствующие) значения.
Возможно, именно поэтому некоторые умные головы предполагают, что “использование Optional
для полей и параметров является плохой практикой”, а инструменты статического анализа показывают предупреждения о таком использовании Optional
.
Решением может быть написание вашей собственной версии Может быть,
и использовать его вместо этого. Существует несколько различных реализаций. Я использую one из моей библиотеки Reactive Toolbox Core .
Резюме
Необязательный
/ Вариант
/ Возможно,
позволяет инженерам Java использовать следующее соглашение:
- Каждая переменная/поле/параметр с простым типом не является null и никогда не может быть.
- Каждая переменная/поле/параметр, тип которых заключен в
Необязательный
/Вариант
/Возможно,
– потенциально может быть пустым. - Если какая-либо внешняя библиотека/вызов может возвращать
нулевое
значение, результат должен быть немедленно завернут вНеобязательно
/Вариант
/Может быть
.
Без каких-либо дополнительных усилий строгое следование приведенному выше соглашению позволяет писать Java-код, который:
- никогда не вызывает
NullPointerException
и все значенияnull
обрабатываются должным образом. - четко и явно различает нулевые и ненулевые значения в коде и позволяет компилятору применять это различие во время компиляции.
Оригинал: “https://dev.to/siy/consistent-null-values-handling-in-java-3c5e”