Как избежать NPE?

Посмотреть в Telegram: @JavaSobes/185
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.