Использование в requireNonNull()
качестве первых утверждений в методе позволяет прямо / быстро определить причину исключения.
Трассировка стека четко указывает на то, что исключение было сгенерировано сразу после ввода метода, поскольку вызывающая сторона не соблюдала требования / контракт.
Передача null
объекта другому методу может действительно вызывать исключение за один раз, но причина проблемы может быть более сложной для понимания, поскольку исключение будет вызвано определенным вызовом null
объекта, который может быть намного дальше.
Вот конкретный и реальный пример, который показывает, почему мы должны отдавать предпочтение быстрым сбоям в целом и, в частности, используя Object.requireNonNull()
или каким-либо образом выполнять проверку на нулевое значение для параметров, предназначенных для этого null
.
Предположим, что Dictionary
класс , который сочиняет LookupService
и List
из String
представляющих слова , содержащиеся в. Эти поля предназначены для не null
и один из них передается в Dictionary
конструктор.
Теперь предположим «плохую» реализацию Dictionary
без null
проверки в записи метода (здесь это конструктор):
public class Dictionary {
private final List<String> words;
private final LookupService lookupService;
public Dictionary(List<String> words) {
this.words = this.words;
this.lookupService = new LookupService(words);
}
public boolean isFirstElement(String userData) {
return lookupService.isFirstElement(userData);
}
}
public class LookupService {
List<String> words;
public LookupService(List<String> words) {
this.words = words;
}
public boolean isFirstElement(String userData) {
return words.get(0).contains(userData);
}
}
Теперь давайте вызовем Dictionary
конструктор со null
ссылкой на words
параметр:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
JVM бросает NPE в этом заявлении:
return words.get(0).contains(userData);
Исключение в потоке "main" java.lang.NullPointerException
в LookupService.isFirstElement (LookupService.java:5)
в Dictionary.isFirstElement (Dictionary.java:15)
at Dictionary.main (Dictionary.java:22)
Исключение вызывается в LookupService
классе, а его происхождение намного раньше ( Dictionary
конструктор). Это делает общий анализ проблем гораздо менее очевидным.
Есть words
null
? Есть words.get(0) null
? Обе ? Почему один, другой или, может быть, оба null
? Это ошибка кодирования в Dictionary
(конструктор? Вызванный метод?)? Это ошибка кодирования в LookupService
? (конструктор? вызванный метод?)?
Наконец, нам нужно будет проверить больше кода, чтобы найти источник ошибки, а в более сложном классе, возможно, даже использовать отладчик, чтобы легче понять, что это произошло.
Но почему простая вещь (отсутствие проверки нуля) становится сложной проблемой?
Представьте себе, что
Потому что мы допустили начальную ошибку / отсутствие идентифицируемой утечки конкретного компонента на нижних компонентах.
LookupService
Был ли это не локальный сервис, а удаленный сервис или сторонняя библиотека с небольшим количеством отладочной информации, или вы предполагаете, что у вас не было 2, а 4 или 5 уровней вызовов объектов до того, как null
их обнаружат? Проблема была бы еще более сложной для анализа.
Таким образом, способ одолеть это
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
Таким образом, нет головной боли: мы получаем исключение, как только это получено:
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Исключение в потоке "main" java.lang.NullPointerException
в java.util.Objects.requireNonNull (Objects.java:203)
на com.Dictionary. (Dictionary.java:15)
на com.Dictionary.main (Dictionary.java:24)
Обратите внимание, что здесь я проиллюстрировал проблему с помощью конструктора, но вызов метода мог иметь такое же ограничение проверки нуля.