Null vérifier la chaîne de vs attraper NullPointerException
Un service web renvoie un énorme XML et j'ai besoin d'accéder profondément imbriqués les champs d'elle. Par exemple:
return wsObject.getFoo().getBar().getBaz().getInt()
Le problème est que getFoo()
, getBar()
, getBaz()
peuvent renvoyer toutes null
.
Cependant, si je vérifie pour null
dans tous les cas, le code devient très détaillé et difficile à lire. De plus, j'ai peut-être raté l'vérifie pour certains champs.
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
//maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
Est-il acceptable d'écrire
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
ou serait-ce considéré comme un antipattern?
- Je n'aurais pas l'esprit de la
null
vérifie que beaucoup, depuiswsObject.getFoo().getBar().getBaz().getInt()
est déjà une odeur de code. Lisez ce que le "la Loi de Déméter" est et préfèrent restructurer le code en conséquence. Alors le problème de lanull
vérifie sera parti. Et pensez à utiliserOptional
. - Ce sujet de l'utilisation de XPath et en le laissant à leur évaluation?
- Ce code est probablement généré par
wsdl2java
, qui n'a aucun respect pour la Loi de Déméter.
Vous devez vous connecter pour publier un commentaire.
Attraper
NullPointerException
est un vraiment problématique chose à faire car ils peuvent se produire presque n'importe où. Il est très facile d'en obtenir un à partir d'un bug, l'attraper par accident et continuer comme si tout est normal, donc se cachait un véritable problème. C'est tellement difficile de traiter avec de sorte qu'il est préférable de l'éviter complètement. (Par exemple, pensez à l'auto-unboxing d'une valeur nullInteger
.)Je vous suggérons d'utiliser le
Facultatif
classe à la place. C'est souvent la meilleure approche pour travailler avec des valeurs qui sont soit présente, soit absente.L'aide que vous pouvez écrire votre code comme ceci:
Pourquoi facultatif?
À l'aide de
Optional
s au lieu denull
pour les valeurs qui peuvent être absents le rend très visible et claire pour les lecteurs, et le type de système assurez-vous que vous n'avez pas accidentellement oublier.Vous obtenez également l'accès à des méthodes de travail avec ces valeurs de façon plus pratique, comme
map
etorElse
.Est l'absence valide ou une erreur?
Mais penser aussi que si c'est un résultat valide pour l'intermédiaire des méthodes pour renvoyer la valeur null ou si c'est un signe d'une erreur. Si c'est toujours une erreur, alors il est probablement mieux de se débarrasser de l'exception que de retourner une valeur particulière, ou par l'intermédiaire des méthodes d'eux-mêmes pour lever une exception.
Peut-être plus d'options?
Si en revanche absente de valeurs à partir de l'intermédiaire de méthodes sont valables, peut-être que vous pouvez passer à
Optional
s pour eux aussi?Puis vous pouvez les utiliser comme ceci:
Pourquoi ne pas en option?
La seule raison pour laquelle je peux penser pour ne pas utiliser
Optional
est de savoir si cela est vraiment dans une critique pour les performances de la partie du code, et si la collecte des déchets généraux s'avère être un problème. C'est parce que quelquesOptional
objets sont alloués à chaque fois que le code est exécuté, et la VM pourrait ne pas être en mesure d'optimiser ceux qui sont loin. Dans ce cas, l'original de votre si-tests peut-être mieux.Try
au lieu deOptional
. Bien qu'il n'y a pas deTry
dans l'API Java, il existe de nombreuses bibliothèques fournissant un, par exemple, javaslang.io, github.com/bradleyscollins/try4j, functionaljava.org ou github.com/jasongoodwin/better-java-monadsFClass::getBar
etc serait plus courte.static
méthode, qui va subir une très pénalité mineure.Foo::getBar
vous indique immédiatement que c'est une méthode sur unFoo
, ce qui n'est pas évident avec un lambda. Mais sur la troisième maingetBar
pourrait être une méthode statique, ou pourrait prendre plus d'un argument, etc, les choses sont claires avec un lambda, mais pas avec une meth-réf. Je n'ai pas été en mesure de former une solide opinion sur qui est le meilleur chemin.J'proposons de considérer
Objets.requireNonNull(T obj, String message)
. Vous pouvez créer des chaînes avec un message détaillé pour chaque exception, commeJe vous suggère de ne pas utiliser la déclaration spéciale-valeurs, comme
-1
. Ce n'est pas un style Java. Java a été conçu le mécanisme des exceptions pour éviter de cette façon ancienne qui est venu du langage C.Jeter
NullPointerException
n'est pas la meilleure option. Vous pouvez fournir votre propre exception (ce qui vérifié de garantir qu'il sera manipulé par un utilisateur ou décoché pour le traiter d'une manière plus facile) ou l'utilisation d'une exception spécifique à partir de XML parser vous utilisez.Objects.requireNonNull
finalement jetteNullPointerException
. Donc, ce n'est pas faire de la situation toute différente de celle quereturn wsObject.getFoo().getBar().getBaz().getInt()
if
s que l'OP a montréOptional
classe, ou peut-être de retour nullableInteger
En supposant que la structure de classe est en effet hors de notre contrôle, comme cela semble être le cas, je pense que la capture de la NPE, comme suggéré dans la question est en effet une solution raisonnable, à moins que la performance est une préoccupation majeure. Une petite amélioration pourrait être pour envelopper le lancer/attraper logique pour éviter l'encombrement:
Maintenant, vous pouvez tout simplement faire:
Comme l'a déjà souligné Tom dans le commentaire,
Déclaration suivante désobéit à la La loi de Déméter,
Ce que vous voulez est
int
et vous pouvez l'obtenir à partir d'Foo
. Loi de Déméter dit, de ne jamais parler aux étrangers. Pour votre cas, vous pouvez masquer la réelle mise en œuvre sous le capot deFoo
etBar
.Maintenant, vous pouvez créer de la méthode dans
Foo
chercherint
deBaz
. En fin de compte,Foo
auraBar
et dansBar
, nous pouvons accéder àInt
sans exposerBaz
directement àFoo
. Donc, null contrôles sont probablement divisé en différentes classes et seuls les attributs requis seront partagés entre les classes.null
vérifier de ses propres sous-balises.Ma réponse est presque dans la même ligne que @comités janki, mais je voudrais modifier le code extrait légèrement comme ci-dessous:
Vous pouvez ajouter un null vérifier
wsObject
ainsi, si il n'y a aucune chance de l'objet est null.Pour améliorer la lisibilité, vous pouvez utiliser plusieurs variables, comme
Vous dire que certaines méthodes "peut renvoyer
null
" mais ne dis pas dans quelles circonstances ils reviennentnull
. Vous dites que vous attraper leNullPointerException
mais vous ne dites pas pourquoi vous l'attraper. Ce manque d'information suggère que vous n'avez pas une compréhension claire de ce que les exceptions sont et pourquoi ils sont supérieurs à l'alternative.Envisager une méthode de classe est destinée à effectuer une action, mais la méthode peut pas garantie il va effectuer une action, en raison de circonstances indépendantes de sa volonté (qui est en fait pour tous méthodes en Java). Nous appelons cette méthode et qu'elle renvoie. Le code qui appelle la méthode doit savoir si elle a réussi. Comment peut-il savoir? Comment peut-il être structuré de manière à faire face avec les deux possibilités de réussite ou d'échec?
À l'aide d'exceptions, on peut écrire des méthodes qui ont succès en tant que post-condition. Si la méthode retourne, il a réussi. Si elle lève une exception, il avait échoué. C'est une grande victoire pour plus de clarté. Nous pouvons écrire le code qui clairement les processus de la normale, en cas de réussite, et de déplacer tout le code de gestion d'erreur dans
catch
clauses. Il apparaît souvent que les détails de comment ou pourquoi une méthode a échoué ne sont pas importants pour l'appelant, de sorte que le mêmecatch
clause peut être utilisée pour le traitement de plusieurs types de défaillance. Et il arrive souvent qu'une méthode n'a pas besoin d'intercepter des exceptions à tous les, mais de leur permettre de se propager à son de l'appelant. Les Exceptions en raison du programme de bugs sont dans cette dernière catégorie; quelques méthodes peuvent réagir de façon appropriée lorsqu'il y a un bug.Donc, ces méthodes qui retournent des
null
.null
valeur indiquer un bug dans votre code? Si elle le fait, vous ne devriez pas être la capture de l'exception à tous. Et votre code ne doit pas être en essayant de deuxième deviner lui-même. Il suffit d'écrire ce qui est clair et concis sur l'hypothèse que cela va fonctionner. Est une chaîne d'appels de méthode claire et concise? Puis il suffit de les utiliser.null
indiquent l'entrée non valide votre programme? Si c'est le cas, uneNullPointerException
n'est pas une exception appropriée à jeter, parce que traditionnellement il est réservé pour indiquer les bugs. Vous voulez probablement pour lever une exception personnalisée dérivé deIllegalArgumentException
(si vous voulez un décochée exception) ouIOException
(si vous voulez un checked exception). Est-ce votre programme requis pour fournir des informations détaillées de la syntaxe des messages d'erreur lorsque il y a une entrée non valide? Si oui, la vérification de chaque méthode pour unnull
valeur de retour puis jetant un diagnostic approprié exception est la seule chose que vous pouvez faire. Si votre programme n'a pas besoin de fournir des diagnostics détaillés, enchaînant les appels de méthode ensemble, attraper desNullPointerException
, puis de le jeter vos exception personnalisée est la plus claire et la plus concise.L'une des réponses les revendications que l'enchaînement des appels de méthode de violer les Loi de Déméter et sont donc mauvais. Cette affirmation est erronée.
Ne pas attraper
NullPointerException
. Vous ne savez pas où il veut en venir (je sais qu'il n'est pas probable dans votre cas, mais peut-être quelque chose d'autre l'a lancé) et il est lent.Vous souhaitez accéder au champ spécifié et pour cela, tous les autres domaines doit pas être null. C'est une excellente raison valable de vérifier chaque champ. Je serais probablement le vérifier dans l'une, puis de créer une méthode pour des raisons de lisibilité. Comme d'autres l'ont souligné déjà de retour -1 est très oldschool mais je ne sais pas si vous avez raison ou non (par exemple, parler à un autre système).
Edit: On peut se demander si c'est disobeyes la Loi De Demeter depuis le WsObject est probablement qu'une structure de données (vérifier https://stackoverflow.com/a/26021695/1528880).
Si vous ne voulez pas de refactoriser le code et vous pouvez utiliser Java 8, il est possible d'utiliser la Méthode de référence.
Une simple première démo (excusez la statique inner classes)
Sortie
L'interface
Getter
est juste une interface fonctionnelle, vous pouvez utiliser un équivalent.GetterResult
classe, les accesseurs sont supprimés pour des raisons de clarté, maintenez la suite de la lecture de la chaîne, le cas échéant, ou de l'indice de la dernière getter appelé.La méthode
getterChain
est un simple passe-partout morceau de code, qui peuvent être générées automatiquement (ou manuellement en cas de besoin).J'ai structuré le code de sorte que la répétition d'un bloc est évident.
Ce n'est pas une solution parfaite, comme vous avez encore besoin de définir une surcharge de
getterChain
par nombre d'accesseurs.Je refactoriser le code à la place, mais si vous pouvez pas, et vous trouvez votre auto à l'aide de longues getter chaînes souvent, vous pouvez envisager la construction d'une classe avec les surcharges que prendre de 2 à 10, les accesseurs.
Comme d'autres l'ont dit, le respect de la Loi de Déméter est certainement partie de la solution. Une autre partie, dans la mesure du possible, est de changer ces enchaîné les méthodes de sorte qu'ils ne peuvent pas retourner
null
. Vous pouvez éviter de revenirnull
par au lieu de retourner un videString
, un videCollection
, ou quelque autre objet factice que cela signifie ou fait ce que l'appelant ne avecnull
.Je voudrais ajouter une réponse qui mettent l'accent sur la sens de l'erreur. Exception nulle en elle-même ne fournit pas de sens complet de l'erreur. Donc, je vous conseille d'éviter de traiter directement avec eux.
Il y a des milliers de cas où votre code peut aller mal: impossible de se connecter à la base de données IO Exception, erreur de Réseau... Si vous les traiter un par un (comme le nul voir ici), ce serait trop de tracas.
Dans le code:
Même lorsque vous savez que le champ est nul, vous n'avez aucune idée sur ce qui va mal. Peut-être que la Barre est nul, mais est-il prévu? Ou est-ce une erreur de données? Pensez aux gens qui lisent votre code
Comme dans xenteros réponse, j'aimerais proposer l'aide de personnalisé décoché exception. Par exemple, dans cette situation: Foo peut être null (données valides), mais la Barre et Baz ne doit jamais être nul (données non valides)
Le code peut être ré-écrit:
NullPointerException
est une exception d'exécution, donc en général n'est pas recommandé pour l'attraper, mais pour l'éviter.Vous devrez attraper l'exception partout où vous voulez l'appeler la méthode (ou il sera propagée vers le haut de la pile). Néanmoins, si dans votre cas vous pouvez continuer à travailler avec ce résultat avec la valeur -1, et vous êtes sûr qu'il ne se propagent pas parce que vous n'êtes pas en utilisant l'une des "pièces" qui peut être null, alors il semble que le droit de me rattraper
Edit:
Je suis d'accord avec la plus tard réponse de @xenteros, il sera préférable de lancer votre propre exception au lieu de retourner -1 vous pouvez l'appeler
InvalidXMLException
par exemple.Ont suivi ce post depuis hier.
J'ai été de commentaire/vote les commentaires qui dit, la capture de NPE est mauvais. Ici, c'est pourquoi je l'ai fait.
Sortie
3.216
0.002
Je vois un gagnant clair ici. Avoir si la vérification est beaucoup trop moins cher que de prendre une exception. J'ai vu que Java 8 façon de faire. Considérant que 70% des applications actuelles continuent d'utiliser Java-7-je suis l'ajout de cette réponse.
Ligne de fond Pour toutes les applications à mission critique, la manipulation des NPE est coûteux.
Si l'efficacité est un problème, alors la "attraper" l'option doit être considérée.
Si "attraper" ne peut pas être utilisé car il se propageraient (comme mentionné par 'SCouto') puis utiliser des variables locales pour éviter la multiplication des appels à des méthodes
getFoo()
,getBar()
etgetBaz()
.Il est utile d'envisager de créer votre propre Exception. Appelons ça de la MyOperationFailedException. Vous pouvez le jeter au lieu de retourner une valeur. Le résultat sera le même - vous allez quitter la fonction, mais vous ne retourne pas de valeur codée en dur -1 ce qui est de Java anti-modèle. En Java, nous utilisons des Exceptions.
EDIT:
En fonction de la discussion dans les commentaires, permettez-moi d'ajouter quelque chose à mes pensées précédentes. Dans ce code il y a deux possibilités. L'une est que vous acceptez nulle et l'autre est, que c'est une erreur.
Si c'est une erreur et il se produit, Vous pouvez déboguer votre code à l'aide d'autres structures à des fins de débogage lorsque les points d'arrêt ne sont pas assez.
Si c'est acceptable, vous ne se soucient pas où cette valeur null est apparu. Si vous le faites, vous ne devriez pas la chaîne de ces demandes.
La méthode que vous avez est longue, mais très lisible. Si j'étais un nouveau développeur pour en venir à votre base de code, je pouvais voir ce que vous avez fait assez rapidement. La plupart des autres réponses (y compris la capture de l'exception) ne semblent pas être de rendre les choses plus lisibles et certains sont ce qui rend moins lisible à mon avis.
Étant donné qu'il est probable que vous n'avez pas de contrôle sur la source générée et en supposant que vous avez vraiment besoin juste d'accéder en quelques profondément imbriqués les champs, ici et là, alors je vous recommande d'emballage chaque profondément imbriqués l'accès à une méthode.
Si vous trouvez vous-même la rédaction d'un grand nombre de ces méthodes, ou si vous trouvez vous-même tenté de faire de ces méthodes statiques publiques, alors je voudrais créer un objet distinct de modèle, nichée à la façon dont vous le souhaitez, avec uniquement les champs dont vous vous souciez, et de convertir de l'objet modèle de votre modèle d'objet.
Lorsque vous communiquez avec un service web distant, il est tout à fait normal qu'un "domaine distant" et un "domaine d'application" et de basculer entre les deux. La distance de domaine est souvent limitée par le protocole web (par exemple, vous ne pouvez pas envoyer des méthodes d'assistance en arrière dans un pur service RESTful et profondément imbriqués modèles d'objet sont communes à éviter la multiplication des appels de l'API) et n'est donc pas idéal pour une utilisation directe à votre client.
Par exemple:
par l'application de la Loi de Déméter,
Donner une réponse qui semble différent de tous les autres.
Raison :
Pour rendre votre code facile à lire, essayez ceci pour la vérification des conditions :
EDIT :
Toutes les suggestions seront appréciées..!!
wsObject
contient la valeur de retour de Webservice..!! Le Service sera appelé déjà etwsObject
obtiendrez une longueXML
de données comme un webservice de réponse..!! Donc, il n'y a rien comme serveur situé sur un autre continent parce quegetFoo()
est juste un élément de l'obtention de méthode de lecture ce n'est pas un Webservice appel..!! @xenterosgetFoo
,getBar
,getBaz
. la seule chose que nous pouvons obtenir à partir de la question estgetFoo().getBar().getBaz().getInt()
est un Entier. C'est la raison pour laquelle je n'ai pas utilisé les variables ici..!!J'ai écrit une classe appelée
Snag
qui permet de définir un chemin d'accès pour naviguer dans une arborescence d'objets. Voici un exemple de son utilisation:Ce qui signifie que l'instance
ENGINE_NAME
serait effectivement appelCar?.getEngine()?.getName()
sur l'instance est passé, et le retournull
cas échéant la référence retournéenull
:Il n'est pas publié sur Maven, mais si quelqu'un trouve cela utile il ici (sans garantie bien sûr!)
C'est un peu basique mais il semble faire le travail. Bien évidemment, il est plus obsolète avec les plus récentes versions de Java et d'autres JVM langages qui prennent en charge la sécurité de la navigation ou
Optional
.