NullPointerException – самое частое исключение в Java. По
некоторым данным, это порядка 70% от всех логгируемых исключений. Тони Хоар, создатель понятия Null Reference, назвал свое изобретение
«Ошибка на миллиард долларов». К сожалению, у неё нет универсального решения, но есть рекомендации по борьбе.
Первым делом, если API не подразумевает работу с null-параметрами, каждый его метод должен начинаться с
проверок на null и последующих исключений. Или хотя бы с
assert-а. Фактически это заменит NPE на исключение другого типа, но такой код будет выглядеть как
предварительное условие вызова, а особое исключение или текст сообщения ассерта лучше объяснит произошедшее.
Следующий шаг –
юнит-тесты. Не важно, считаются ли null-аргументы штатной или исключительной ситуацией – хорошие тесты обязаны фиксировать реакцию компонентов программы на
null.
Уже давно победить NPE hell помогают инструменты
статического анализа кода. Этап анализа обычно включается в процесс сборки, наряду с тестами. Все современные IDE предоставляют аннотации
@Nullable/
@NonNull для явного указания nullability. С версии Java 9 аннотации для статического анализа в рамках стандарта
JSR-305 попали в пакет
javax.annotation.
В
современных языках, таких как Kotlin, анализ встроен в компилятор. Информация о nullability является частью объявления типа, а попытка передать зануляемое значение в незануляемый параметр приведет к несовместимости типов и ошибке компиляции. Вдобавок, языки снабжаются множеством наллобезопасных операторов вроде
?:.
Как справедливо заметили коллеги в чате-обсуждении, в предыдущем посте не был упомянут класс Optional, исправляемся.Другое направление разрешения проблемы NPE – отказ от значения
null вообще, и использование вместо этого класса-обертки
Optional. Такой класс существовал раньше в различных библиотеках (например Google Guava), а с Java версии 8 стал частью стандарта.
Optional – более объектно-ориентированный путь обработки отсутствия значения. Пустое значение тоже является полноценным объектом, и вместо языковых конструкций для
null, обработка пустоты выполняется обычными методами
orElse*.
Optional близок к паттерну ООП
Null Object.
Любое nullable значение можно превратить в
Optional вызовом его фабричного метода
ofNullable(). Также класс предоставляет методы создания заведомо непустого значения
of() со встроенной проверкой на
null, и
empty() для заведомо пустышки.
Класс
Optional относительно свежий и активно развивается – в каждой версии Java в него добавлялись новые вспомогательные методы, с самого его появления и до Java 11.