J'ai du code qui échoue à cause de NullPointerException. Une méthode est appelée sur l'objet là où il n'existe pas.
Cependant, cela m'a amené à réfléchir à la meilleure façon de résoudre ce problème. Dois-je toujours coder de manière défensive pour les valeurs nulles afin que je code à l'épreuve du temps pour les exceptions de pointeur nul, ou dois-je corriger la cause de la valeur null afin qu'elle ne se produise pas en aval.
Quelles sont vos pensées?
Réponses:
Si null est un paramètre d'entrée raisonnable pour votre méthode, corrigez la méthode. Sinon, corrigez l'appelant. "Raisonnable" est un terme flexible, je propose donc le test suivant: Comment la méthode doit-elle suspendre une entrée nulle? Si vous trouvez plus d'une réponse possible, alors null n'est pas une entrée raisonnable.
la source
Precondition.checkNotNull(...)
. Voir stackoverflow.com/questions/3022319/…IllegalArgumentException
lorsque null est donné. Cela signale aux appelants de la méthode que le bogue est dans leur code (plutôt que dans la méthode elle-même).N'utilisez pas null, utilisez facultatif
Comme vous l'avez souligné, l'un des plus gros problèmes avec
null
Java est qu'il peut être utilisé partout , ou du moins pour tous les types de référence.Il est impossible de dire ce qui pourrait être
null
et ce qui ne pourrait pas l'être.Java 8 contient un bien meilleur motif:
Optional
.Et un exemple d'Oracle:
Si chacune d'elles peut ou non retourner une valeur réussie, vous pouvez changer les API en
Optional
s:En encodant explicitement le caractère facultatif du type, vos interfaces seront bien meilleures et votre code sera plus propre.
Si vous n'utilisez pas Java 8, vous pouvez regarder
com.google.common.base.Optional
dans Google Guava.Une bonne explication par l'équipe de Guava: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained
Une explication plus générale des inconvénients à null, avec des exemples de plusieurs langues: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/
@Nonnull, @Nullable
Java 8 ajoute ces annotations pour aider les outils de vérification de code comme les IDE à détecter les problèmes. Leur efficacité est assez limitée.
Vérifiez quand cela a du sens
N'écrivez pas 50% de votre code en vérifiant null, en particulier s'il n'y a rien de sensé que votre code puisse faire avec une
null
valeur.D'un autre côté, s'il
null
pourrait être utilisé et signifier quelque chose, assurez-vous de l'utiliser.En fin de compte, vous ne pouvez évidemment pas supprimer
null
de Java. Je recommande fortement de remplacer l'Optional
abstraction autant que possible et de vérifiernull
ces autres fois que vous pouvez faire quelque chose de raisonnable à ce sujet.la source
NullPointerException
? UnNullPointerException
peut se produire littéralement chaque fois que vous appelez une méthode d'instance en Java. Vous auriezthrows NullPointerException
dans presque toutes les méthodes jamais.Il y a plusieurs façons de gérer cela, poivrer votre code
if (obj != null) {}
n'est pas idéal, c'est désordonné, cela ajoute du bruit lors de la lecture du code plus tard pendant le cycle de maintenance et est sujet aux erreurs, car il est facile d'oublier de faire cet emballage de plaque de chaudière.Cela dépend si vous voulez que le code continue à s'exécuter en silence ou échoue. Est-ce
null
une erreur ou une condition attendue.Ce qui est nul
Dans chaque définition et chaque cas, Null représente le manque absolu de données. Les valeurs nulles dans les bases de données représentent l'absence de valeur pour cette colonne. un null
String
n'est pas la même chose qu'un videString
, un nullint
n'est pas la même chose que ZERO, en théorie. En pratique "ça dépend". VideString
peut faire une bonneNull Object
implémentation pour la classe String, carInteger
cela dépend de la logique métier.Les alternatives sont:
Le
Null Object
motif. Créez une instance de votre objet qui représente l'null
état et initialisez toutes les références à ce type avec une référence à l'Null
implémentation. Ceci est utile pour les objets de type valeur simple qui n'ont pas beaucoup de références à d'autres objets qui pourraient également êtrenull
et devraient êtrenull
comme un état valide.Utilisez des outils orientés aspect pour tisser des méthodes avec un
Null Checker
aspect qui empêche les paramètres d'être nuls. C'est pour les cas oùnull
est une erreur.Utilisez
assert()
pas beaucoup mieux que leif (obj != null){}
mais moins de bruit.Utilisez un outil d'application des contrats tel que Contracts For Java . Même cas d'utilisation que quelque chose comme AspectJ mais plus récent et utilise des annotations au lieu de fichiers de configuration externes. Le meilleur des deux œuvres d'Aspects et Asserts.
1 est la solution idéale lorsque les données entrantes sont connues
null
et doivent être remplacées par une valeur par défaut afin que les consommateurs en amont n'aient pas à gérer tout le code passe-partout à vérification nulle. La vérification par rapport aux valeurs par défaut connues sera également plus expressive.2, 3 et 4 ne sont que des générateurs d'exceptions alternatifs pratiques à remplacer
NullPointerException
par quelque chose de plus informatif, ce qui est toujours une amélioration.À la fin
null
en Java est presque dans tous les cas une erreur logique. Vous devez toujours vous efforcer d'éliminer la cause profonde deNullPointerExceptions
. Vous devez vous efforcer de ne pas utiliser lesnull
conditions comme logique métier.if (x == null) { i = someDefault; }
effectuez simplement l'assignation initiale à cette instance d'objet par défaut.la source
null
soit inattendu, c'est une vraie erreur, alors tout devrait s'arrêter complètement.null == null
(même en PHP), mais dans les bases de données,null != null
car dans les bases de données, il représente une valeur inconnue plutôt que "rien". Deux inconnues ne sont pas nécessairement égales, tandis que deux riens sont égaux.L'ajout de contrôles nuls peut rendre le test problématique. Voir cette grande conférence ...
Consultez la conférence Google Tech: "Les pourparlers sur le code propre - ne cherchez rien!" il en parle vers la minute 24
http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E
La programmation paranoïaque consiste à ajouter des contrôles nuls partout. Cela semble être une bonne idée au début, mais du point de vue des tests, cela rend votre test de type de vérification nulle difficile à gérer.
De plus, lorsque vous créez une condition préalable à l'existence d'un objet tel que
cela vous empêche de créer House car vous allez lever une exception. Supposons que vos cas de test créent des objets fictifs pour tester autre chose que Door, eh bien, vous ne pouvez pas le faire car Door est requis
Ceux qui ont souffert de l'enfer de la création factice sont bien conscients de ces types de désagréments.
En résumé, votre suite de tests doit être suffisamment robuste pour tester les portes, les maisons, les toits ou autre sans avoir à être paranoïaque à ce sujet. Sérieusement, à quel point est-il difficile d'ajouter un test de vérification nulle pour des objets spécifiques dans vos tests :)
Vous devriez toujours préférer les applications qui fonctionnent parce que vous avez plusieurs tests qui PROUVENT que cela fonctionne, plutôt que d'espérer que cela fonctionne simplement parce que vous avez tout un tas de vérifications nulles pré-conditionnelles partout
la source
tl; dr - il est bon de vérifier les
null
s inattendus mais MAUVAIS pour une application pour essayer de les rendre bons.Détails
De toute évidence, il existe des situations où
null
une entrée ou une sortie est valide pour une méthode, et d'autres où elle ne l'est pas.Règle 1:
Règle n ° 2:
Étant donné une spécification claire du «contrat» d'une méthode vis-à-vis des
null
s, c'est une erreur de programmation de passer ou de renvoyer unnull
où vous ne devriez pas.Règle n ° 3:
Si une méthode détecte une
null
qui ne devrait pas exister, elle ne doit pas tenter de résoudre le problème en la transformant en autre chose. Cela cache simplement le problème au programmeur. Au lieu de cela, cela devrait permettre au NPE de se produire et provoquer une défaillance afin que le programmeur puisse déterminer la cause principale et la corriger. Espérons que l'échec sera remarqué lors des tests. Sinon, cela en dit long sur votre méthodologie de test.Règle n ° 4:
Si vous avez des bogues dans votre code qui entraînent de nombreux NPE, la chose la plus difficile peut être de déterminer d'où
null
viennent les valeurs. Une façon de faciliter le diagnostic consiste à écrire votre code afin qu'ilnull
soit détecté le plus tôt possible. Souvent, vous pouvez le faire en conjonction avec d'autres vérifications; par exemple(Il y a évidemment des cas où les règles 3 et 4 doivent être tempérées par la réalité. Par exemple (règle 3), certains types d'applications doivent tenter de continuer après avoir détecté probablement des erreurs de programmation. Et (règle 4) trop de vérification de mauvais paramètres peut avoir un impact sur les performances.)
la source
Je recommanderais de fixer la méthode comme défensive. Par exemple:
Devrait être plus dans le sens de ceci:
Je me rends compte que c'est absolument trivial, mais si l'invocateur attend un objet, donnez-lui un objet par défaut qui ne fera pas passer un null.
la source
new String()
du tout.null
s est la pire option possible, pire que de lancer un NPE.Les règles générales suivantes concernant null m'ont beaucoup aidé jusqu'à présent:
Si les données proviennent de l'extérieur de votre contrôle, vérifiez systématiquement les valeurs nulles et agissez de manière appropriée. Cela signifie soit lever une exception qui a du sens pour la fonction (cochée ou décochée, assurez-vous simplement que le nom de l'exception vous indique exactement ce qui se passe.). Mais ne craignez JAMAIS une valeur lâche dans votre système qui peut potentiellement porter des surprises.
Si Null se trouve dans le domaine des valeurs appropriées pour votre modèle de données, traitez-le de manière appropriée.
Lorsque vous retournez des valeurs, essayez de ne pas renvoyer de valeurs nulles autant que possible. Préférez toujours les listes vides, les chaînes vides, les modèles d'objets nuls. Conservez les valeurs nulles comme valeurs renvoyées lorsqu'il s'agit de la meilleure représentation possible des données pour un cas d'utilisation donné.
Probablement le plus important de tous ... Tests, tests et test à nouveau. Lorsque vous testez votre code, ne le testez pas en tant que codeur, testez-le en tant que dominatrice psychopathe nazie et essayez d'imaginer toutes sortes de façons de torturer l'enfer hors de ce code.
Cela tend un peu du côté paranoïaque en ce qui concerne les null conduisant souvent à des façades et à des mandataires qui interfacent les systèmes avec le monde extérieur et des valeurs strictement contrôlées à l'intérieur avec une redondance abondante. Le monde extérieur signifie ici à peu près tout ce que je n'ai pas codé moi-même. Il supporte un coût d'exécution mais jusqu'à présent, j'ai rarement eu à l'optimiser en créant des "sections null safe" de code. Je dois dire cependant que je crée principalement des systèmes de longue durée pour les soins de santé et la dernière chose que je veux, c'est que le sous-système d'interface transportant vos allergies à l'iode au scanner CT se bloque à cause d'un pointeur nul inattendu parce que quelqu'un d'autre dans un autre système n'a jamais réalisé que les noms pouvaient contiennent des apostrophes ou des caractères comme 但 耒耨。
de toute façon .... mes 2 cents
la source
Je proposerais d'utiliser le modèle Option / Some / None à partir des langages fonctionnels. Je ne suis pas un spécialiste Java, mais j'utilise intensivement ma propre implémentation de ce modèle dans mon projet C #, et je suis sûr qu'il pourrait être converti en monde Java.
L'idée derrière ce modèle est: s'il s'agit logiquement d'avoir une situation où il y a possibilité d'absence d'une valeur (par exemple lors de la récupération de la base de données par id) vous fournissez un objet de type Option [T], où T est une valeur possible . I cas d'absence d'une valeur objet de classe None [T] retourné, dans le cas si existence de la valeur - objet de Some [T] retourné, contenant la valeur.
Dans ce cas, vous devez gérer la possibilité d'absence de valeur, et si vous effectuez un examen du code, vous pourriez facilement trouver un placec de manipulation incorrecte. Pour vous inspirer de l'implémentation du langage C #, reportez-vous à mon référentiel bitbucket https://bitbucket.org/mikegirkin/optionsomenone
Si vous renvoyez une valeur nulle et qu'elle est logiquement équivalente à une erreur (par exemple, aucun fichier n'existe ou n'a pas pu se connecter), vous devez lever une exception ou utiliser un autre modèle de gestion des erreurs. L'idée derrière cela, encore une fois, vous vous retrouvez avec une solution, lorsque vous devez gérer la situation d'absence de valeur, et que vous pouvez facilement trouver les endroits de mauvaise manipulation dans le code.
la source
Eh bien, si l'un des résultats possibles de votre méthode est une valeur nulle, vous devez coder de manière défensive pour cela, mais si une méthode est censée renvoyer une valeur non nulle, mais je ne le corrigerais pas à coup sûr.
Comme d'habitude, cela dépend du cas, comme pour la plupart des choses de la vie :)
la source
Les bibliothèques lang d' Apache Commons fournissent un moyen de gérer les valeurs nulles
la méthode defaultIfNull dans la classe ObjectUtils vous permet de retourner une valeur par défaut si l'objet passé est null
la source
N'utilisez pas le type facultatif, à moins qu'il ne soit vraiment facultatif, la sortie est souvent mieux gérée comme exception, à moins que vous ne vous attendiez vraiment à ce que null soit une option, et non parce que vous écrivez régulièrement du code bogué.
Le problème n'est pas nul en tant que type utilisé, comme le souligne l'article de Google. Le problème est que les valeurs nulles doivent être vérifiées et gérées, souvent elles peuvent être fermées avec élégance.
Il existe un certain nombre de cas nuls qui représentent des conditions non valides dans des zones en dehors du fonctionnement de routine du programme (entrée utilisateur non valide, problèmes de base de données, panne de réseau, fichiers manquants, données corrompues), c'est à cela que servent les exceptions vérifiées, en les gérant, même si c'est juste pour l'enregistrer.
La gestion des exceptions est autorisée dans la JVM selon différentes priorités et privilèges opérationnels, par opposition à un type, comme Facultatif, qui tient compte de la nature exceptionnelle de leur occurrence, y compris la prise en charge précoce du chargement différé prioritaire du gestionnaire en mémoire, car vous ne le faites pas. besoin de traîner tout le temps.
Vous n'avez pas besoin d'écrire des gestionnaires nuls partout, seulement là où ils sont susceptibles de se produire, partout où vous accédez potentiellement à un service de données non fiable, et puisque la plupart d'entre eux se divisent en quelques modèles généraux qui peuvent facilement être résumés, vous n'avez vraiment besoin que un appel de gestionnaire, sauf dans de rares cas.
Je suppose donc que ma réponse serait de l'envelopper dans une exception vérifiée et de la gérer, ou de corriger le code non fiable s'il est à votre portée.
la source