Lors de la lecture du code source JDK, je trouve courant que l'auteur vérifie les paramètres s'ils sont nuls, puis lance manuellement une nouvelle NullPointerException (). Pourquoi font-ils cela? Je pense qu'il n'est pas nécessaire de le faire car il lancera une nouvelle NullPointerException () quand il appellera une méthode. (Voici un code source de HashMap, par exemple :)
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
java
nullpointerexception
LiJiaming
la source
la source
ArgumentNullException
dans des cas comme ça (plutôt queNullReferenceException
) - c'est en fait une très bonne question de savoir pourquoi vous soulevezNullPointerException
explicitement ici (plutôt que différent).IllegalArgumentException
ouNullPointerException
pour un argument nul. La convention JDK est la dernière.Réponses:
Plusieurs raisons me viennent à l'esprit, plusieurs étant étroitement liées:
Échec rapide: s'il échoue, mieux vaut échouer le plus tôt possible. Cela permet aux problèmes d'être capturés plus près de leur source, ce qui les rend plus faciles à identifier et à récupérer. Cela évite également de gaspiller des cycles de processeur sur du code qui échouera.
Intention: le fait de lancer l'exception indique clairement aux responsables de la maintenance que l'erreur est là intentionnellement et que l'auteur était conscient des conséquences.
Cohérence: si l'erreur pouvait se produire naturellement, elle pourrait ne pas se produire dans tous les scénarios. Si aucun mappage n'est trouvé, par exemple,
remappingFunction
ne sera jamais utilisé et l'exception ne sera pas levée. La validation préalable des entrées permet un comportement plus déterministe et une documentation plus claire .Stabilité: le code évolue avec le temps. Un code qui rencontre une exception peut naturellement, après un peu de refactorisation, cesser de le faire ou le faire dans des circonstances différentes. Le jeter explicitement rend moins susceptible que le comportement change par inadvertance.
la source
new NullPointerException(message)
constructeur pour clarifier ce qui est nul. Idéal pour les personnes qui n'ont pas accès à votre code source. Ils en ont même fait un one-liner dans JDK 8 avec laObjects.requireNonNull(object, message)
méthode utilitaire.C'est pour la clarté, la cohérence et pour éviter que des travaux supplémentaires et inutiles ne soient effectués.
Considérez ce qui se passerait s'il n'y avait pas de clause de garde en haut de la méthode. Il appellerait toujours
hash(key)
etgetNode(hash, key)
même s'ilnull
avait été passéremappingFunction
avant que le NPE ne soit jeté.Pire encore, si la
if
condition estfalse
alors nous prenons laelse
branche, qui n'utilise pas duremappingFunction
tout le, ce qui signifie que la méthode ne lance pas toujours NPE quand anull
est passé; cela dépend de l'état de la carte.Les deux scénarios sont mauvais. If
null
n'est pas une valeur valide pourremappingFunction
la méthode doit systématiquement lever une exception quel que soit l'état interne de l'objet au moment de l'appel, et il doit le faire sans faire de travail inutile qui est inutile étant donné qu'il va simplement lancer. Enfin, c'est un bon principe de code propre et clair d'avoir la garde à l'avant afin que quiconque examine le code source puisse facilement voir qu'il le fera.Même si l'exception était actuellement levée par chaque branche de code, il est possible qu'une future révision du code change cela. Effectuer le contrôle au début garantit qu'il sera définitivement exécuté.
la source
En plus des raisons énumérées par l'excellente réponse de @ shmosel ...
Performances: il peut y avoir / y avoir eu des avantages en termes de performances (sur certaines machines virtuelles Java) à lancer le NPE explicitement plutôt que de laisser la machine virtuelle Java le faire.
Cela dépend de la stratégie adoptée par l'interpréteur Java et le compilateur JIT pour détecter le déréférencement des pointeurs nuls. Une stratégie consiste à ne pas tester null, mais à piéger le SIGSEGV qui se produit lorsqu'une instruction essaie d'accéder à l'adresse 0. C'est l'approche la plus rapide dans le cas où la référence est toujours valide, mais elle est coûteuse dans le cas NPE.
Un test explicite pour
null
dans le code éviterait l'atteinte des performances SIGSEGV dans un scénario où les NPE étaient fréquents.(Je doute que ce soit une micro-optimisation valable dans une JVM moderne, mais cela aurait pu être dans le passé.)
Compatibilité: la raison probable pour laquelle il n'y a pas de message dans l'exception est la compatibilité avec les NPE qui sont lancés par la JVM elle-même. Dans une implémentation Java conforme, un NPE lancé par la JVM contient un
null
message. (Android Java est différent.)la source
Outre ce que d'autres personnes ont souligné, il convient de noter ici le rôle de la convention. En C #, par exemple, vous avez également la même convention de lever explicitement une exception dans des cas comme celui-ci, mais c'est spécifiquement un
ArgumentNullException
, qui est un peu plus spécifique. (La convention C # est que représenteNullReferenceException
toujours un bogue d'un type quelconque - tout simplement, cela ne devrait jamais se produire dans le code de production; d'accord, le faitArgumentNullException
généralement aussi, mais cela pourrait être un bogue plus du genre "vous ne le faites pas comprendre comment utiliser correctement la librairie "genre de bogue).Donc, fondamentalement, en C #
NullReferenceException
signifie que votre programme a réellement essayé de l'utiliser, alors queArgumentNullException
cela signifie qu'il a reconnu que la valeur était fausse et qu'il n'a même pas pris la peine d'essayer de l'utiliser. Les implications peuvent en fait être différentes (selon les circonstances) car celaArgumentNullException
signifie que la méthode en question n'a pas encore eu d'effets secondaires (puisqu'elle a échoué aux conditions préalables de la méthode).Incidemment, si vous augmentez quelque chose comme
ArgumentNullException
ouIllegalArgumentException
, cela fait partie du point de faire le contrôle: vous voulez une exception différente de celle que vous obtiendriez "normalement".Dans tous les cas, la levée explicite de l'exception renforce la bonne pratique consistant à être explicite sur les pré-conditions et les arguments attendus de votre méthode, ce qui facilite la lecture, l'utilisation et la maintenance du code. Si vous n'avez pas vérifié explicitement
null
, je ne sais pas si c'est parce que vous pensiez que personne ne passerait jamais unnull
argument, vous le comptez pour lever l'exception de toute façon, ou vous avez simplement oublié de vérifier cela.la source
null
objet." Je n'en suis pas fou, mais cela semble être le design prévu.NullReferenceException
(équivalent àNullPointerException
) signifie que votre code a réellement essayé de l' utiliser (ce qui est toujours un bogue - cela ne devrait jamais arriver dans le code de production), contre "Je sais que l'argument est faux, donc je n'ai même pas essayez de l'utiliser. " Il y a aussiArgumentException
(ce qui signifie que l'argument était faux pour une autre raison).C'est ainsi que vous obtiendrez l'exception dès que vous perpétrez l'erreur, plutôt que plus tard lorsque vous utilisez la carte et que vous ne comprendrez pas pourquoi cela s'est produit.
la source
Cela transforme une condition d'erreur apparemment erratique en une violation de contrat claire: la fonction a certaines conditions préalables pour fonctionner correctement, elle les vérifie donc au préalable, les imposant de les respecter.
L'effet est que vous n'aurez pas à déboguer
computeIfPresent()
lorsque vous en retirerez l'exception. Une fois que vous voyez que l'exception provient de la vérification des conditions préalables, vous savez que vous avez appelé la fonction avec un argument non autorisé. Si la vérification n'était pas là, vous devriez exclure la possibilité qu'il y ait un bogue encomputeIfPresent()
lui-même qui conduit à la levée de l'exception.De toute évidence, lancer le générique
NullPointerException
est un très mauvais choix, car cela ne signale pas une violation du contrat en soi.IllegalArgumentException
serait un meilleur choix.Sidenote:
Je ne sais pas si Java le permet (j'en doute), mais les programmeurs C / C ++ utilisent
assert()
dans ce cas, ce qui est nettement meilleur pour le débogage: il dit au programme de planter immédiatement et aussi fort que possible si le condition évaluée à faux. Donc, si tu couraissous un débogueur, et quelque chose est passé
NULL
dans l'un ou l'autre des arguments, le programme s'arrêterait juste à la ligne indiquant quel argument étaitNULL
, et vous seriez capable d'examiner toutes les variables locales de la pile d'appels entière à loisir.la source
assert something != null;
mais cela nécessite l'-assertions
indicateur lors de l'exécution de l'application. Si le-assertions
drapeau n'est pas là, le mot clé assert ne lancera pas une AssertionExceptionC'est parce qu'il est possible que cela ne se produise pas naturellement. Voyons un morceau de code comme celui-ci:
(bien sûr, il y a de nombreux problèmes dans cet exemple concernant la qualité du code. Désolé d'être paresseux d'imaginer un exemple parfait)
Situation où
c
ne sera pas réellement utilisé etNullPointerException
ne sera pas lancé même sic == null
c'est possible.Dans des situations plus compliquées, il devient très difficile de traquer de tels cas. C'est pourquoi une vérification générale comme
if (c == null) throw new NullPointerException()
c'est mieux.la source
Il est intentionnel de protéger d'autres dommages ou d'entrer dans un état incohérent.
la source
En dehors de toutes les autres excellentes réponses ici, je voudrais également ajouter quelques cas.
Vous pouvez ajouter un message si vous créez votre propre exception
Si vous lancez le vôtre,
NullPointerException
vous pouvez ajouter un message (ce que vous devriez certainement!)Le message par défaut est un
null
fromnew NullPointerException()
et toutes les méthodes qui l'utilisent, par exempleObjects.requireNonNull
. Si vous imprimez ce null, il peut même se traduire en une chaîne vide ...Un peu court et peu informatif ...
La trace de la pile donnera beaucoup d'informations, mais pour que l'utilisateur sache ce qui était nul, il doit extraire le code et regarder la ligne exacte.
Imaginez maintenant que NPE soit enveloppé et envoyé sur le net, par exemple sous la forme d'un message dans une erreur de service Web, peut-être entre différents services ou même des organisations. Dans le pire des cas, personne ne peut comprendre ce que
null
signifie ...Les appels de méthode en chaîne vous permettront de deviner
Une exception vous indiquera uniquement sur quelle ligne l'exception s'est produite. Considérez la ligne suivante:
Si vous obtenez un NPE et qu'il pointe sur cette ligne, lequel de
repository
etsomeObject
était nul?Au lieu de cela, vérifier ces variables lorsque vous les obtenez pointera au moins vers une ligne où elles sont, espérons-le, la seule variable gérée. Et, comme mentionné précédemment, encore mieux si votre message d'erreur contient le nom de la variable ou similaire.
Les erreurs lors du traitement d'un grand nombre d'entrées devraient donner des informations d'identification
Imaginez que votre programme traite un fichier d'entrée avec des milliers de lignes et qu'il y a soudainement une NullPointerException. Vous regardez l'endroit et réalisez que certaines entrées étaient incorrectes ... quelle entrée? Vous aurez besoin de plus d'informations sur le numéro de ligne, peut-être la colonne ou même le texte de la ligne entière pour comprendre quelle ligne de ce fichier doit être corrigée.
la source