Использование в 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)
Обратите внимание, что здесь я проиллюстрировал проблему с помощью конструктора, но вызов метода мог иметь такое же ограничение проверки нуля.