Utiliser requireNonNull()
comme premières instructions dans une méthode permet d'identifier immédiatement / rapidement la cause de l'exception.
Le stacktrace indique clairement que l'exception a été levée dès l'entrée de la méthode car l'appelant n'a pas respecté les exigences / contrat.
Passer un null
objet à une autre méthode peut en effet provoquer une exception à la fois mais la cause du problème peut être plus compliquée à comprendre car l'exception sera lancée dans une invocation spécifique sur l' null
objet qui peut être beaucoup plus loin.
Voici un exemple concret et réel qui montre pourquoi nous devons favoriser l'échec rapide en général et plus particulièrement en utilisant Object.requireNonNull()
ou de toute manière pour effectuer une vérification non nulle sur des paramètres conçus pour ne pas l'être null
.
Supposons une Dictionary
classe qui compose un LookupService
et un List
de String
représenter les mots contenus dans. Ces champs sont conçus pour ne pas l'être null
et l'un d'eux est passé dans le Dictionary
constructeur.
Supposons maintenant une "mauvaise" implémentation de Dictionary
sans null
vérification dans l'entrée de méthode (ici c'est le constructeur):
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);
}
}
Maintenant, appelons le Dictionary
constructeur avec une null
référence pour le words
paramètre:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
La JVM jette le NPE à cette déclaration:
return words.get(0).contains(userData);
Exception dans le thread "main" java.lang.NullPointerException
sur LookupService.isFirstElement (LookupService.java:5)
à Dictionary.isFirstElement (Dictionary.java:15)
sur Dictionary.main (Dictionary.java:22)
L'exception est déclenchée dans la LookupService
classe alors que son origine est bien antérieure (le Dictionary
constructeur). Cela rend l'analyse globale des problèmes beaucoup moins évidente.
Est-ce words
null
? Est-ce words.get(0) null
? Tous les deux ? Pourquoi l'un, l'autre ou peut-être les deux null
? S'agit-il d'une erreur de codage dans Dictionary
(constructeur? Méthode invoquée?)? S'agit-il d'une erreur de codage qui n'était pas un service local mais un service distant ou une bibliothèque tierce avec peu d'informations de débogage ou imaginez que vous n'aviez pas 2 couches mais 4 ou 5 couches d'appels d'objets avant celaLookupService
? (constructeur? méthode invoquée?)?
Enfin, nous devrons inspecter plus de code pour trouver l'origine de l'erreur et dans une classe plus complexe, peut-être même utiliser un débogueur pour comprendre plus facilement ce qui s'est passé.
Mais pourquoi une chose simple (un manque de contrôle nul) devient un problème complexe?
Parce que nous avons autorisé le bug / manque initial identifiable sur une fuite de composant spécifique sur les composants inférieurs.
Imaginez que ce être détectées? Le problème serait encore plus complexe à analyser. LookupService
null
La façon de favoriser est donc:
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
De cette façon, pas de maux de tête: nous obtenons l'exception levée dès qu'elle est reçue:
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Exception dans le thread "main" java.lang.NullPointerException
sur java.util.Objects.requireNonNull (Objects.java:203)
sur com.Dictionary. (Dictionary.java:15)
à com.Dictionary.main (Dictionary.java:24)
Notez qu'ici j'ai illustré le problème avec un constructeur mais un appel de méthode pourrait avoir la même contrainte de vérification non nulle.