Un service Web renvoie un énorme XML et j'ai besoin d'accéder à ses champs profondément imbriqués. Par exemple:
return wsObject.getFoo().getBar().getBaz().getInt()
Le problème est que getFoo()
, getBar()
, getBaz()
peut tout retour null
.
Cependant, si je vérifie null
dans tous les cas, le code devient très détaillé et difficile à lire. De plus, je peux manquer les contrôles 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 cela serait-il considéré comme un anti-modèle?
java
exception
nullpointerexception
null
custom-error-handling
David Frank
la source
la source
null
vérifications ne me dérangeraient pas autant, carwsObject.getFoo().getBar().getBaz().getInt()
c'est déjà une odeur de code. Lisez ce qu'est la «loi de Demeter» et préférez refactoriser votre code en conséquence. Ensuite, le problème avec lesnull
chèques disparaîtra également. Et pensez à utiliserOptional
.wsdl2java
, qui n'a aucun respect pour la loi de Déméter.Réponses:
La capture
NullPointerException
est une chose vraiment problématique à faire car elle peut se produire presque n'importe où. Il est très facile d'en obtenir un à partir d'un bug, de l'attraper par accident et de continuer comme si tout était normal, cachant ainsi un vrai problème. C'est tellement difficile à gérer, il est donc préférable d'éviter complètement. (Par exemple, pensez au déballage automatique d'un nullInteger
.)Je vous suggère d'utiliser la
Optional
classe à la place. C'est souvent la meilleure approche lorsque vous souhaitez travailler avec des valeurs présentes ou absentes.En utilisant cela, vous pouvez écrire votre code comme ceci:
Pourquoi facultatif?
L'utilisation de
Optional
s au lieu denull
pour des valeurs qui pourraient être absentes rend ce fait très visible et clair pour les lecteurs, et le système de type s'assurera que vous ne l'oublierez pas accidentellement.Vous avez également accès à des méthodes pour travailler plus facilement avec de telles valeurs, comme
map
etorElse
.L'absence est-elle valide ou une erreur?
Mais pensez également à savoir s'il s'agit d'un résultat valide pour que les méthodes intermédiaires retournent null ou si cela est le signe d'une erreur. S'il s'agit toujours d'une erreur, il est probablement préférable de lancer une exception que de renvoyer une valeur spéciale, ou que les méthodes intermédiaires elles-mêmes lancent une exception.
Peut-être plus d'options?
Si par contre les valeurs absentes des méthodes intermédiaires sont valides, vous pouvez peut-être passer à
Optional
s pour elles également?Ensuite, vous pouvez les utiliser comme ceci:
Pourquoi pas facultatif?
La seule raison à laquelle je peux penser pour ne pas utiliser
Optional
est si cela se trouve dans une partie vraiment critique des performances du code, et si la surcharge du garbage collection s'avère être un problème. En effet, quelquesOptional
objets sont alloués à chaque fois que le code est exécuté et la machine virtuelle peut ne pas être en mesure de les optimiser. Dans ce cas, vos tests if d'origine pourraient être meilleurs.la source
Try
place deOptional
. Bien qu'il n'yTry
en ait pas dans l'API Java, de nombreuses bibliothèques en fournissent une, par exemple javaslang.io , github.com/bradleyscollins/try4j , functionaljava.org ou github.com/jasongoodwin/better-java-monadsFClass::getBar
etc. serait plus court.static
méthode, ce qui entraînera une pénalité très mineure.Je suggère d'envisager
Objects.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 de valeurs de retour spéciales, comme
-1
. Ce n'est pas un style Java. Java a conçu le mécanisme des exceptions pour éviter cette méthode démodée issue du langage C.Le lancer
NullPointerException
n'est pas non plus la meilleure option. Vous pouvez fournir votre propre exception (en la vérifiant pour garantir qu'elle sera traitée par un utilisateur ou décochée pour la traiter plus facilement) ou utiliser une exception spécifique de l'analyseur XML que vous utilisez.la source
Objects.requireNonNull
jette finalementNullPointerException
. Donc, cela ne rend pas la situation différente de cellereturn wsObject.getFoo().getBar().getBaz().getInt()
if
s comme l'a montré OPOptional
classe, ou peut-être de renvoyer unInteger
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 du NPE comme suggéré dans la question est en effet une solution raisonnable, à moins que la performance ne soit une préoccupation majeure. Une petite amélioration pourrait être d'encapsuler la logique lancer / attraper pour éviter l'encombrement:
Maintenant, vous pouvez simplement faire:
la source
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), "");
ne donne pas d'erreur de compilation qui pourrait être problématique.Comme l'a déjà souligné Tom dans le commentaire,
La déclaration suivante désobéit à la loi de Déméter ,
Ce que vous voulez, c'est
int
et vous pouvez l'obtenirFoo
. Law of Demeter dit, ne parlez jamais aux étrangers . Pour votre cas, vous pouvez cacher la mise en œuvre réelle sous le capot deFoo
etBar
.Maintenant, vous pouvez créer méthode
Foo
pour aller chercher àint
partirBaz
. En fin de compte,Foo
auraBar
et dansBar
nous pouvons accéderInt
sans exposerBaz
directement àFoo
. Ainsi, les vérifications nulles sont probablement divisées en différentes classes et seuls les attributs requis seront partagés entre les classes.la source
null
vérification de ses propres sous-balises.Ma réponse va presque dans la même ligne que @janki, mais j'aimerais modifier légèrement l'extrait de code comme ci-dessous:
Vous pouvez également ajouter une vérification de null
wsObject
, s'il y a une chance que cet objet soit nul.la source
Vous dites que certaines méthodes «peuvent revenir
null
» mais ne dites pas dans quelles circonstances elles reviennentnull
. Vous dites que vous attrapez leNullPointerException
mais vous ne dites pas pourquoi vous l'attrapez. Ce manque d'informations suggère que vous ne comprenez pas clairement à quoi servent les exceptions et pourquoi elles sont supérieures à l'alternative.Considérez une méthode de classe qui est destinée à effectuer une action, mais la méthode ne peut pas garantir qu'elle exécutera l'action, en raison de circonstances indépendantes de sa volonté (ce qui est en fait le cas pour toutes les méthodes en Java ). Nous appelons cette méthode et elle revient. Le code qui appelle cette méthode doit savoir si elle a réussi. Comment peut-il savoir? Comment peut-il être structuré pour faire face aux deux possibilités, de succès ou d'échec?
En utilisant des exceptions, nous pouvons écrire des méthodes qui ont du succès en tant que post-condition . Si la méthode retourne, elle a réussi. S'il lève une exception, il a échoué. C'est une grande victoire pour la clarté. Nous pouvons écrire du code qui traite clairement le cas normal de réussite et déplacer tout le code de gestion des erreurs dans des
catch
clauses. Il apparaît souvent que les détails sur la manière ou la raison pour laquelle une méthode a échoué ne sont pas importants pour l'appelant, de sorte que la mêmecatch
clause peut être utilisée pour gérer plusieurs types d'échec. Et il arrive souvent qu'une méthode n'a pas besoin d'exceptions de capture du tout , mais peut simplement leur permettre de se propager à son interlocuteur. Les exceptions dues à des bogues de programme sont dans cette dernière classe; peu de méthodes peuvent réagir de manière appropriée en cas de bogue.Donc, ces méthodes qui reviennent
null
.null
valeur indique- t-elle un bogue dans votre code? Si c'est le cas, vous ne devriez pas du tout attraper l'exception. Et votre code ne doit pas essayer de se remettre en question. Écrivez simplement ce qui est clair et concis en supposant que cela fonctionnera. Une chaîne d'appels de méthode est-elle claire et concise? Alors utilisez-les simplement.null
valeur indique- t-elle une entrée invalide dans votre programme? Si c'est le cas, aNullPointerException
n'est pas une exception appropriée à lever, car il est conventionnellement réservé à l'indication de bogues. Vous souhaitez probablement lever une exception personnalisée dérivée deIllegalArgumentException
(si vous voulez une exception non cochée ) ouIOException
(si vous voulez une exception cochée). Votre programme doit-il fournir des messages d'erreur de syntaxe détaillés en cas d'entrée non valide? Si tel est le cas, vérifier chaque méthode pour unenull
valeur de retour, puis lancer une exception de diagnostic appropriée est la seule chose que vous pouvez faire. Si votre programme n'a pas besoin de fournir des diagnostics détaillés, ilNullPointerException
est plus clair et plus concis de chaîner les appels de méthode, d'en attraper puis de lancer votre exception personnalisée.L'une des réponses prétend que les appels de méthode en chaîne violent la loi de Déméter et sont donc mauvais. Cette affirmation est erronée.
la source
Pour améliorer la lisibilité, vous pouvez utiliser plusieurs variables, comme
la source
N'attrape pas
NullPointerException
. Vous ne savez pas d'où cela vient (je sais que ce n'est pas probable dans votre cas mais peut-être que quelque chose d'autre l'a jeté) et c'est lent. Vous souhaitez accéder au champ spécifié et pour cela, tous les autres champs ne doivent pas être nuls. C'est une raison valable parfaite pour vérifier chaque champ. Je le vérifierais probablement dans un si et puis créerais une méthode pour la lisibilité. Comme d'autres l'ont souligné, le retour de -1 est déjà très ancien mais je ne sais pas si vous avez une raison ou non (par exemple, parler à un autre système).Edit: Il est discutable s'il désobéit à la loi de Demeter puisque le WsObject n'est probablement qu'une structure de données (consultez https://stackoverflow.com/a/26021695/1528880 ).
la source
Si vous ne souhaitez pas refactoriser le code et que vous pouvez utiliser Java 8, il est possible d'utiliser des références de méthode.
Une simple démo d'abord (excusez les classes internes statiques)
Production
L'interface
Getter
n'est qu'une interface fonctionnelle, vous pouvez utiliser n'importe quel équivalent.GetterResult
class, les accesseurs supprimés pour plus de clarté, contiennent le résultat de la chaîne getter, le cas échéant, ou l'index du dernier getter appelé.La méthode
getterChain
est un morceau de code simple et standard, qui peut être généré automatiquement (ou manuellement si nécessaire).J'ai structuré le code pour que le bloc répétitif soit évident.
Ce n'est pas une solution parfaite car vous devez toujours définir une surcharge de
getterChain
par nombre de getters.Je refactoriserais le code à la place, mais si vous ne pouvez pas et que vous vous trouvez en utilisant souvent de longues chaînes getter, vous pouvez souvent envisager de créer une classe avec des surcharges qui prennent de 2 à, disons, 10 getters.
la source
Comme d'autres l'ont dit, le respect de la loi de Déméter fait définitivement partie de la solution. Une autre partie, dans la mesure du possible, consiste à modifier ces méthodes chaînées afin qu'elles ne puissent pas revenir
null
. Vous pouvez éviter de retournernull
en retournant à la place un objet videString
, videCollection
ou un autre objet factice qui signifie ou fait tout ce que l'appelant feraitnull
.la source
Je voudrais ajouter une réponse qui se concentre sur la signification de l'erreur . L'exception nulle en elle-même ne fournit aucune erreur complète de signification. Je vous conseille donc d'éviter de traiter directement avec eux.
Il y a des milliers de cas où votre code peut mal tourner: impossible de se connecter à la base de données, exception IO, erreur réseau ... Si vous les traitez un par un (comme le contrôle nul ici), ce serait trop compliqué.
Dans le code:
Même lorsque vous savez quel champ est nul, vous n'avez aucune idée de ce qui ne va pas. Peut-être que Bar est nul, mais est-il attendu? Ou est-ce une erreur de données? Pensez aux personnes qui lisent votre code
Comme dans la réponse de xenteros, je proposerais d'utiliser une exception personnalisée non cochée . Par exemple, dans cette situation: Foo peut être nul (données valides), mais Bar et Baz ne doivent jamais être nulles (données non valides)
Le code peut être réécrit:
la source
NullPointerException
est une exception d'exécution, il n'est donc généralement pas recommandé de l'attraper, mais de l'éviter.Vous devrez intercepter l'exception là où vous voulez appeler la méthode (ou elle se propagera dans la pile). Néanmoins, si dans votre cas vous pouvez continuer à travailler avec ce résultat avec la valeur -1 et que vous êtes sûr qu'il ne se propagera pas parce que vous n'utilisez aucun des "morceaux" qui peuvent être nuls, alors il me semble juste de attrape ça
Éditer:
Je suis d'accord avec la réponse ultérieure de @xenteros, il vaudra mieux lancer votre propre exception au lieu de renvoyer -1, vous pouvez l'appeler
InvalidXMLException
par exemple.la source
Je suis ce post depuis hier.
J'ai commenté / voté les commentaires qui disent, attraper NPE est mauvais. Voici pourquoi je fais cela.
Production
3,216
0,002
Je vois un gagnant clair ici. Avoir des chèques if est bien trop moins cher que d'attraper une exception. J'ai vu cette façon de faire Java-8. Considérant que 70% des applications actuelles fonctionnent toujours sur Java-7, j'ajoute cette réponse.
Conclusion Pour toutes les applications critiques, la gestion des NPE est coûteuse.
la source
Si l'efficacité est un problème, l'option «catch» doit être envisagée. Si 'catch' ne peut pas être utilisé car il se propagerait (comme mentionné par 'SCouto') alors utilisez des variables locales pour éviter plusieurs appels aux méthodes
getFoo()
,getBar()
etgetBaz()
.la source
Cela vaut la peine d'envisager de créer votre propre exception. Appelons cela MyOperationFailedException. Vous pouvez le lancer à la place en renvoyant une valeur. Le résultat sera le même - vous quitterez la fonction, mais vous ne retournerez pas la valeur codée en dur -1 qui est l'anti-pattern Java. En Java, nous utilisons des exceptions.
ÉDITER:
D'après la discussion dans les commentaires, permettez-moi d'ajouter quelque chose à mes réflexions précédentes. Dans ce code, il y a deux possibilités. L'une est que vous acceptez null et l'autre est que c'est une erreur.
S'il s'agit d'une erreur et que cela se produit, vous pouvez déboguer votre code en utilisant d'autres structures à des fins de débogage lorsque les points d'arrêt ne suffisent pas.
Si c'est acceptable, vous ne vous souciez pas de l'endroit où ce null est apparu. Si vous le faites, vous ne devriez certainement pas enchaîner ces demandes.
la source
La méthode que vous avez est longue, mais très lisible. Si j'étais un nouveau développeur venant dans votre base de code, je pourrais voir ce que vous faisiez assez rapidement. La plupart des autres réponses (y compris la capture de l'exception) ne semblent pas rendre les choses plus lisibles et certaines le rendent moins lisible à mon avis.
Étant donné que vous n'avez probablement pas de contrôle sur la source générée et en supposant que vous avez vraiment besoin d'accéder à quelques champs profondément imbriqués ici et là, je recommanderais d'encapsuler chaque accès profondément imbriqué avec une méthode.
Si vous vous retrouvez à écrire beaucoup de ces méthodes ou si vous êtes tenté de rendre ces méthodes statiques publiques, je créerais un modèle d'objet séparé, imbriqué comme vous le souhaitez, avec uniquement les champs qui vous intéressent, et convertirais à partir du Web services du modèle objet à votre modèle objet.
Lorsque vous communiquez avec un service Web distant, il est très courant d'avoir un «domaine distant» et un «domaine d'application» et de basculer entre les deux. Le domaine distant est souvent limité par le protocole Web (par exemple, vous ne pouvez pas envoyer de méthodes d'assistance dans un service RESTful pur et les modèles d'objet profondément imbriqués sont courants pour éviter plusieurs appels d'API) et donc pas idéal pour une utilisation directe dans votre client.
Par exemple:
la source
en appliquant la loi de Déméter,
la source
Donner une réponse qui semble différente de toutes les autres.
Raison:
Pour rendre votre code facile à lire, essayez ceci pour vérifier les conditions:
ÉDITER :
Toute suggestion sera appréciée..!!
la source
wsObject
contiendra la valeur renvoyée par Webservice .. !! Le service sera déjà appelé etwsObject
obtiendra une longueXML
donnée en réponse au service Web .. !! Il n'y a donc rien de tel qu'un serveur situé sur un autre continent cargetFoo()
c'est juste un élément obtenant une méthode getter et non un appel Webservice .. !! @xenterosJ'ai écrit une classe appelée
Snag
qui vous permet de définir un chemin pour naviguer dans un arbre d'objets. Voici un exemple de son utilisation:Cela signifie que l'instance
ENGINE_NAME
appellerait effectivementCar?.getEngine()?.getName()
l'instance qui lui a été transmise, et retourneraitnull
si une référence retournaitnull
:Il n'est pas publié sur Maven mais si quelqu'un trouve cela utile, c'est ici (sans garantie bien sûr!)
C'est un peu basique mais cela semble faire le travail. De toute évidence, il est plus obsolète avec les versions plus récentes de Java et d'autres langages JVM qui prennent en charge la navigation sécurisée ou
Optional
.la source