Użycie requireNonNull()jako pierwszych instrukcji w metodzie pozwala na natychmiastowe zidentyfikowanie przyczyny wyjątku.
Stacktrace wyraźnie wskazuje, że wyjątek został zgłoszony natychmiast po wprowadzeniu metody, ponieważ program wywołujący nie przestrzegał wymagań / kontraktu.
Przekazanie nullobiektu do innej metody może rzeczywiście wywołać wyjątek na raz, ale przyczyna problemu może być bardziej skomplikowana do zrozumienia, ponieważ wyjątek zostanie zgłoszony w konkretnym wywołaniu nullobiektu, które może być znacznie dalej.
Oto konkretny i prawdziwy przykład, który pokazuje, dlaczego ogólnie musimy sprzyjać szybkiemu niepowodzeniu, a w szczególności za pomocą Object.requireNonNull()lub w jakikolwiek sposób wykonać kontrolę zerową parametrów zaprojektowanych tak, by nie były null.
Załóżmy, że Dictionaryklasa, która komponuje LookupServicei Listod Stringreprezentujący słów zawartych w. Pola te są zaprojektowane, aby nie być null, a jednym z nich jest przekazywana w Dictionary konstruktorze.
Załóżmy teraz, że „zła” implementacja Dictionarybez nullsprawdzania we wpisie metody (tutaj jest to konstruktor):
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);
}
}
Teraz wywołajmy Dictionarykonstruktor z nullreferencją do wordsparametru:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
JVM rzuca NPE na tę instrukcję:
return words.get(0).contains(userData);
Wyjątek w wątku „main” java.lang.NullPointerException
w LookupService.isFirstElement (LookupService.java:5)
at Dictionary.isFirstElement (Dictionary.java:15)
at Dictionary.main (Dictionary.java:22)
Wyjątek jest wyzwalany w LookupServiceklasie, podczas gdy jego pochodzenie jest znacznie wcześniej ( Dictionarykonstruktor). To sprawia, że ogólna analiza problemu jest znacznie mniej oczywista.
Jest words null? Jest words.get(0) null? Obie ? Dlaczego jedno, drugie, a może oba są null? Czy to błąd kodowania w Dictionary(wywoływana metoda konstruktora?)? Czy to błąd kodowania LookupService? (konstruktor? wywołana metoda?)?
Wreszcie będziemy musieli sprawdzić więcej kodu, aby znaleźć źródło błędu, aw bardziej złożonej klasie może nawet użyć debuggera, aby łatwiej zrozumieć, co się stało.
Ale dlaczego prosta rzecz (brak kontroli zerowej) staje się złożonym problemem?
Ponieważ pozwoliliśmy na identyfikację początkowego błędu / braku określonego przecieku na niższych komponentach.
Wyobraź sobie, że LookupServicenie była to usługa lokalna, ale usługa zdalna lub biblioteka strony trzeciej z kilkoma informacjami debugowania lub wyobraź sobie, że nie masz 2 warstw, ale 4 lub 5 warstw wywołań obiektów przed nullwykryciem? Problem byłby jeszcze bardziej złożony do analizy.
Sposobem na faworyzowanie jest:
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
W ten sposób nie ma bólu głowy: wyjątek zostaje zgłoszony natychmiast po otrzymaniu:
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Wyjątek w wątku „main” java.lang.NullPointerException
at java.util.Objects.requireNonNull (Objects.java:203)
w com.Dictionary. (Dictionary.java:15)
at com.Dictionary.main (Dictionary.java:24)
Zauważ, że tutaj zilustrowałem problem z konstruktorem, ale wywołanie metody może mieć takie samo ograniczenie kontroli innej niż null.