En Java (ou dans tout autre langage avec des exceptions vérifiées), lors de la création de votre propre classe d'exceptions, comment décidez-vous si elle doit être vérifiée ou décochée?
Mon instinct est de dire qu'une exception vérifiée serait demandée dans les cas où l'appelant pourrait être en mesure de récupérer d'une manière productive, alors qu'une exception non vérifiée serait plus pour les cas irrécupérables, mais je serais intéressé par les pensées des autres.
java
exception
checked-exceptions
Matt Sheppard
la source
la source
Réponses:
Les exceptions vérifiées sont excellentes, tant que vous comprenez quand elles doivent être utilisées. L'API Java Core ne respecte pas ces règles pour SQLException (et parfois pour IOException), c'est pourquoi elles sont si terribles.
Les exceptions vérifiées doivent être utilisées pour les erreurs prévisibles mais non évitables dont il est raisonnable de se remettre .
Exceptions non vérifiées doivent être utilisées pour tout le reste.
Je vais vous expliquer cela, car la plupart des gens comprennent mal ce que cela signifie.
À moins que l'exception que vous lancez ne remplisse toutes les conditions ci-dessus, elle doit utiliser une exception non vérifiée.
Réévaluer à tous les niveaux : Parfois, la méthode de capture de l'exception vérifiée n'est pas le bon endroit pour gérer l'erreur. Dans ce cas, réfléchissez à ce qui est raisonnable pour vos propres appelants. Si l'exception est prévisible, impossible à prévenir et raisonnable pour eux de récupérer, vous devez alors lancer vous-même une exception vérifiée. Sinon, vous devez encapsuler l'exception dans une exception non cochée. Si vous suivez cette règle, vous vous retrouverez à convertir les exceptions cochées en exceptions non cochées et vice versa en fonction de la couche dans laquelle vous vous trouvez.
Pour les exceptions vérifiées et non contrôlées, utilisez le bon niveau d'abstraction . Par exemple, un référentiel de code avec deux implémentations différentes (base de données et système de fichiers) devrait éviter d'exposer des détails spécifiques à l'implémentation en lançant
SQLException
ouIOException
. Au lieu de cela, il doit envelopper l'exception dans une abstraction qui couvre toutes les implémentations (par exempleRepositoryException
).la source
D' un apprenant Java :
la source
Il existe un argument très solide pour le contraire: n'utilisez jamais d'exceptions vérifiées. J'hésite à prendre parti dans le débat, mais il semble y avoir un large consensus selon lequel l'introduction d'exceptions vérifiées était une mauvaise décision avec le recul. S'il vous plaît, ne tirez pas sur le messager et ne vous référez pas à ces arguments .
la source
foo
est documenté comme lançantbarException
lors de la lecture après la fin d'un fichier et qu'ilfoo
appelle une méthode qui lèvebarException
même s'ilfoo
ne s'y attend pas, le le code qui appellefoo
pensera que la fin du fichier a été atteinte et n'aura aucune idée que quelque chose d'inattendu s'est produit. Je considérerais cette situation comme étant celle où les exceptions vérifiées devraient être les plus utiles, mais c'est aussi le seul cas où le compilateur autorisera les exceptions vérifiées non gérées.Sur tout système assez grand, avec de nombreuses couches, les exceptions vérifiées sont inutiles car, de toute façon, vous avez besoin d'une stratégie de niveau architectural pour gérer la façon dont l'exception sera gérée (utilisez une barrière de défaillance)
Avec des exceptions vérifiées, votre stratégie de gestion des erreurs est micro-gérée et insupportable sur tout grand système.
La plupart du temps, vous ne savez pas si une erreur est "récupérable" car vous ne savez pas dans quelle couche se trouve l'appelant de votre API.
Disons que je crée une API StringToInt qui convertit la représentation sous forme de chaîne d'un entier en un Int. Dois-je lever une exception vérifiée si l'API est appelée avec la chaîne "foo"? Est-il récupérable? Je ne sais pas parce que dans sa couche, l'appelant de mon API StringToInt peut déjà avoir validé l'entrée, et si cette exception est levée, c'est soit un bug soit une corruption de données et elle n'est pas récupérable pour cette couche.
Dans ce cas, l'appelant de l'API ne veut pas intercepter l'exception. Il veut seulement laisser l'exception "bouillonner". Si j'ai choisi une exception vérifiée, cet appelant aura beaucoup de bloc de capture inutile uniquement pour renvoyer artificiellement l'exception.
Ce qui est récupérable dépend la plupart du temps de l'appelant de l'API, pas du rédacteur de l'API. Une API ne doit pas utiliser d'exceptions vérifiées car seules les exceptions non contrôlées permettent de choisir d'intercepter ou d'ignorer une exception.
la source
Vous avez raison.
Des exceptions non vérifiées sont utilisées pour laisser le système tomber en panne rapidement, ce qui est une bonne chose. Vous devez indiquer clairement à quoi s'attend votre méthode pour fonctionner correctement. De cette façon, vous ne pouvez valider l'entrée qu'une seule fois.
Par exemple:
Juste pour mettre un exemple. Le fait est que si le système tombe en panne rapidement, vous saurez où et pourquoi il a échoué. Vous obtiendrez une trace de pile comme:
Et vous saurez ce qui s'est passé. L'AutreClasse de la méthode "delegateTheWork" (à la ligne 4569) a appelé votre classe avec la valeur "exit", même si elle ne devrait pas etc.
Sinon, vous auriez à saupoudrer des validations sur tout votre code et cela est sujet aux erreurs. De plus, il est parfois difficile de suivre ce qui s'est mal passé et vous pouvez vous attendre à des heures de débogage frustrant
La même chose se produit avec NullPointerExceptions. Si vous avez une classe de 700 lignes avec quelque 15 méthodes, qui utilise 30 attributs et aucun d'entre eux ne peut être nul, au lieu de valider dans chacune de ces méthodes la nullité, vous pouvez rendre tous ces attributs en lecture seule et les valider dans le constructeur ou méthode d'usine.
Exceptions vérifiées sont utiles lorsque le programmeur (vous ou vos collègues) a tout fait correctement, validé l'entrée, exécuté des tests et que tout le code est parfait, mais le code se connecte à un service Web tiers qui peut être en panne (ou à un fichier que vous utilisiez a été supprimé par un autre processus externe, etc.). Le service Web peut même être validé avant que la connexion ne soit tentée, mais pendant le transfert de données, quelque chose s'est mal passé.
Dans ce scénario, vous ou vos collègues ne pouvez rien faire pour l'aider. Mais encore faut-il faire quelque chose et ne pas laisser l'application mourir et disparaître aux yeux de l'utilisateur. Vous utilisez une exception vérifiée pour cela et gérez l'exception, que pouvez-vous faire quand cela se produit?, La plupart du temps, juste pour tenter de consigner l'erreur, probablement enregistrer votre travail (le travail de l'application) et présenter un message à l'utilisateur . (Le site blabla est en panne, veuillez réessayer plus tard, etc.)
Si l'exception vérifiée est surutilisée (en ajoutant la "throw Exception" dans toutes les signatures de méthodes), alors votre code deviendra très fragile, car tout le monde ignorera cette exception (car il est trop général) et la qualité du code sera sérieusement compromis.
Si vous abusez d'une exception non contrôlée, quelque chose de similaire se produira. Les utilisateurs de ce code ne savent pas si quelque chose peut mal se passer. Beaucoup de tentatives {...} catch (Throwable t) apparaîtront.
la source
Voici ma «règle d'or finale».
J'utilise:
Par rapport à la réponse précédente, il s'agit d'une justification claire (sur laquelle on peut être d'accord ou en désaccord) pour l'utilisation de l'une ou l'autre (ou des deux) types d'exceptions.
Pour ces deux exceptions, je vais créer ma propre exception non vérifiée et vérifiée pour mon application (une bonne pratique, comme mentionné ici ), à l'exception des exceptions non vérifiées très courantes (comme NullPointerException)
Ainsi, par exemple, le but de cette fonction particulière ci-dessous est de créer (ou d'obtenir s'il existe déjà) un objet,
ce qui signifie:
=> exception non vérifiée, ET effacer le commentaire javadoc pour cette fonction appelée)
(choix du codeur pour le mettre sur le CALLER: le codeur ne vérifiera pas le paramètre nul mais le codeur LE DOCUMENT)
(responsabilité et choix du code de l'appelé, choix qui sera d'un grand intérêt pour l'appelant
=> exception vérifiée car chaque appelant DOIT prendre une décision si l'objet ne peut pas être créé / trouvé, et que la décision doit être exécutée au moment de la compilation: ils ne peuvent pas utiliser cette fonction sans avoir à gérer cette possibilité, c'est-à-dire avec cette exception vérifiée ).
Exemple:
la source
Ce n'est pas seulement une question de capacité à se remettre de l'exception. À mon avis, ce qui importe le plus, c'est de savoir si l'appelant souhaite intercepter l'exception ou non.
Si vous écrivez une bibliothèque à utiliser ailleurs ou une couche de niveau inférieur dans votre application, demandez-vous si l'appelant souhaite intercepter (connaître) votre exception. Si ce n'est pas le cas, utilisez une exception non vérifiée pour ne pas le surcharger inutilement.
C'est la philosophie utilisée par de nombreux frameworks. Spring et hibernate, en particulier, viennent à l'esprit - ils convertissent l'exception vérifiée connue en exception non vérifiée précisément parce que les exceptions vérifiées sont surutilisées en Java. Un exemple auquel je peux penser est l'exception JSONException de json.org, qui est une exception vérifiée et généralement ennuyeuse - elle devrait être décochée, mais le développeur n'a tout simplement pas réfléchi.
Soit dit en passant, la plupart du temps, l'intérêt de l'appelant pour l'exception est directement lié à la capacité de se remettre de l'exception, mais ce n'est pas toujours le cas.
la source
Voici une solution très simple à votre dilemme vérifié / non vérifié.
Règle 1: Considérez une exception non vérifiée comme une condition testable avant l'exécution du code. par exemple…
où x est nul ... ... le code devrait éventuellement avoir ce qui suit ...
Règle 2: Considérez une exception vérifiée comme une condition non testable qui peut se produire pendant l'exécution du code.
… Dans l'exemple ci-dessus, l'URL (google.com) peut ne pas être disponible en raison de la panne du serveur DNS. Même au moment où le serveur DNS fonctionnait et résolvait le nom «google.com» en une adresse IP, si la connexion est établie avec google.com, à tout moment après le mot de passe, le réseau pourrait tomber. Vous ne pouvez tout simplement pas tester le réseau tout le temps avant de lire et d'écrire dans des flux.
Il y a des moments où le code doit simplement s'exécuter avant que nous puissions savoir s'il y a un problème. En forçant les développeurs à écrire leur code de manière à les forcer à gérer ces situations via Checked Exception, je dois donner mon chapeau au créateur de Java qui a inventé ce concept.
En général, presque toutes les API en Java suivent les 2 règles ci-dessus. Si vous essayez d'écrire dans un fichier, le disque peut se remplir avant de terminer l'écriture. Il est possible que d'autres processus aient provoqué la saturation du disque. Il n'y a tout simplement aucun moyen de tester cette situation. Pour ceux qui interagissent avec du matériel où, à tout moment, l'utilisation du matériel peut échouer, les exceptions vérifiées semblent être une solution élégante à ce problème.
Il y a une zone grise à cela. Dans le cas où de nombreux tests sont nécessaires (une déclaration époustouflante si beaucoup de && et ||), l'exception levée sera une CheckedException simplement parce que c'est trop difficile de bien faire les choses - vous ne pouvez tout simplement pas dire ce problème est une erreur de programmation. S'il y a beaucoup moins de 10 tests (par exemple «if (x == null)»), l'erreur de programmation doit être une exception UncheckedException.
Les choses deviennent intéressantes lorsqu'il s'agit d'interprètes de langue. Selon les règles ci-dessus, une erreur de syntaxe doit-elle être considérée comme une exception vérifiée ou non vérifiée? Je dirais que si la syntaxe du langage peut être testée avant d'être exécutée, ce devrait être une exception UncheckedException. Si la langue ne peut pas être testée - similaire à la façon dont le code assembleur s'exécute sur un ordinateur personnel, l'erreur de syntaxe doit être une exception vérifiée.
Les 2 règles ci-dessus supprimeront probablement 90% de votre inquiétude quant au choix. Pour résumer les règles, suivez ce modèle… 1) si le code à exécuter peut être testé avant d'être exécuté pour qu'il s'exécute correctement et si une exception se produit - alias une erreur de programmeur, l'exception doit être une UncheckedException (une sous-classe de RuntimeException ). 2) si le code à exécuter ne peut pas être testé avant d'être exécuté pour qu'il s'exécute correctement, l'exception doit être une exception vérifiée (une sous-classe d'exception).
la source
Vous pouvez l'appeler une exception cochée ou non cochée; cependant, les deux types d'exceptions peuvent être interceptés par le programmeur, donc la meilleure réponse est: écrivez toutes vos exceptions comme non vérifiées et documentez-les. De cette façon, le développeur qui utilise votre API peut choisir s'il veut intercepter cette exception et faire quelque chose. Les exceptions vérifiées sont une perte totale de temps pour tout le monde et cela fait de votre code un cauchemar choquant à regarder. Des tests unitaires appropriés feront ensuite apparaître toutes les exceptions que vous pourriez avoir à attraper et à faire avec.
la source
Exception vérifiée: si le client peut récupérer d'une exception et souhaite continuer, utilisez l'exception vérifiée.
Exception non vérifiée: si un client ne peut rien faire après l'exception, déclenchez une exception non vérifiée.
Exemple: Si vous devez effectuer une opération arithmétique dans une méthode A () et en fonction de la sortie de A (), vous devez effectuer une autre opération. Si la sortie est nulle à partir de la méthode A () à laquelle vous ne vous attendiez pas pendant l'exécution, vous devez alors lever l'exception de pointeur Null qui est une exception d'exécution.
Se référer ici
la source
Je suis d'accord avec la préférence pour les exceptions non contrôlées en règle générale, en particulier lors de la conception d'une API. L'appelant peut toujours choisir d'intercepter une exception documentée et non vérifiée. Vous ne forcez pas inutilement l'appelant à le faire.
Je trouve les exceptions vérifiées utiles au niveau inférieur, comme détail d'implémentation. Cela semble souvent être un meilleur mécanisme de contrôle du flux que d'avoir à gérer un "code retour" d'erreur spécifié. Cela peut parfois aider à voir l'impact d'une idée pour un changement de code de bas niveau aussi ... déclarer une exception vérifiée en aval et voir qui devrait s'ajuster. Ce dernier point ne s'applique pas s'il y a beaucoup de génériques: catch (Exception e) ou throw Exception qui n'est généralement pas trop bien pensé de toute façon.
la source
Voici que je veux partager mon opinion que j'ai après de nombreuses années d'expérience en développement:
Exception vérifiée. Cela fait partie du cas d'utilisation métier ou du flux d'appels, c'est une partie de la logique d'application que nous attendons ou non. Par exemple, la connexion a été rejetée, la condition n'est pas satisfaite, etc. Je l'appelle habituellement exception de post-traitement ou exception "utilisateur".
Exception non vérifiée. Cela fait partie de l'exception de programmation, une erreur de programmation de code logiciel (bogue, défaut) et reflète la manière dont les programmeurs doivent utiliser l'API conformément à la documentation. Si un document de bibliothèque / framework externe dit qu'il s'attend à obtenir des données dans une certaine plage et non null, car NPE ou IllegalArgumentException seront levées, le programmeur devrait s'y attendre et utiliser l'API correctement selon la documentation. Sinon, l'exception sera levée. Je l'appelle habituellement exception de prétraitement ou exception de «validation».
Par public cible. Parlons maintenant du public cible ou du groupe de personnes auquel les exceptions ont été conçues (à mon avis):
Par phase de cycle de vie de développement d'applications.
La raison pour laquelle les frameworks utilisent généralement des exceptions non contrôlées (Spring par exemple) est que le framework ne peut pas déterminer la logique métier de votre application, il appartient aux développeurs de saisir et de concevoir leur propre logique.
la source
Nous devons distinguer ces deux types d'exceptions selon qu'il s'agit d'une erreur de programmeur ou non.
FileNotFoundException est un bon exemple pour comprendre les différences subtiles. FileNotFoundException est levée si le fichier est introuvable. Il y a deux raisons à cette exception. Si le chemin du fichier est défini par le développeur ou par l'utilisateur final via l'interface graphique, il doit s'agir d'une exception non vérifiée. Si le fichier est supprimé par quelqu'un d'autre, il doit s'agir d'une exception vérifiée.
L'exception vérifiée peut être gérée de deux manières. Ceux-ci utilisent try-catch ou propagent l'exception. En cas de propagation d'exception, toutes les méthodes de la pile d'appels seront étroitement couplées en raison de la gestion des exceptions. C'est pourquoi, nous devons utiliser soigneusement l'exception vérifiée.
Dans le cas où vous développez un système d'entreprise en couches, vous devez choisir la plupart du temps l'exception non vérifiée à lancer, mais n'oubliez pas d'utiliser l'exception cochée pour le cas où vous ne pouvez rien faire.
la source
Les exceptions cochées sont utiles dans les cas récupérables où vous souhaitez fournir des informations à l'appelant (par exemple, autorisations insuffisantes, fichier introuvable, etc.).
Les exceptions non vérifiées sont rarement utilisées, voire pas du tout, pour informer l'utilisateur ou le programmeur d'erreurs graves ou de conditions inattendues pendant l'exécution. Ne les jetez pas si vous écrivez du code ou des bibliothèques qui seront utilisées par d'autres, car ils ne s'attendent pas à ce que votre logiciel lève des exceptions non contrôlées car le compilateur ne les force pas à être capturés ou déclarés.
la source
Chaque fois qu'une exception est moins probable, et nous pouvons continuer même après avoir attrapé cela, et nous ne pouvons rien faire pour éviter cette exception, nous pouvons utiliser l'exception vérifiée.
Chaque fois que nous voulons faire quelque chose de significatif lorsqu'une exception particulière se produit et lorsque cette exception est attendue mais pas certaine, nous pouvons utiliser l'exception vérifiée.
Chaque fois qu'une exception navigue dans différentes couches, nous n'avons pas besoin de l'attraper dans chaque couche, dans ce cas, nous pouvons utiliser une exception d'exécution ou une exception d'habillage comme exception non vérifiée.
L'exception d'exécution est utilisée lorsque l'exception est le plus susceptible de se produire, il n'y a aucun moyen d'aller plus loin et rien ne peut être récupéré. Dans ce cas, nous pouvons donc prendre des précautions à l'égard de cette exception. EX: NUllPointerException, ArrayOutofBoundsException. Ce sont plus susceptibles de se produire. Dans ce scénario, nous pouvons prendre des précautions lors du codage pour éviter une telle exception. Sinon, nous devrons écrire des blocs try catch partout.
Des exceptions plus générales peuvent être faites. Non cochées, les moins générales sont vérifiées.
la source
Je pense que nous pouvons penser à des exemptions de plusieurs questions:
pourquoi l'exemption se produit-elle? Que pouvons-nous faire quand cela se produit
par erreur, un bug. comme une méthode d'objet nul est appelée.
Ce type d'exception doit être corrigé pendant le test. Sinon, cela interrompt la production et vous avez un bug très élevé qui doit être corrigé immédiatement. Ce type d'exceptions n'a pas besoin d'être vérifié.
par l'entrée de l'extérieur, vous ne pouvez pas contrôler ou faire confiance à la sortie du service externe.
Ici, vous devrez peut-être vérifier si le nom est nul si vous voulez continuer quand il est nul, sinon, vous pouvez le laisser tranquille et il s'arrêtera ici et donnera à l'appelant l'exception d'exécution. Ce type d'exceptions n'a pas besoin d'être vérifié.
par exception d'exécution externe, vous ne pouvez pas contrôler ou approuver le service externe.
Ici, vous devrez peut-être intercepter toutes les exceptions de ExternalService si vous souhaitez continuer quand cela se produit, sinon, vous pouvez le laisser seul et il s'arrêtera ici et donnera à l'appelant l'exception d'exécution.
par exception vérifiée de l'extérieur, vous ne pouvez pas contrôler ou faire confiance au service externe.
Ici, vous devrez peut-être intercepter toutes les exceptions de ExternalService si vous souhaitez continuer quand cela se produit, sinon, vous pouvez le laisser seul et il s'arrêtera ici et donnera à l'appelant l'exception d'exécution.
Dans ce cas, devons-nous savoir quel type d'exception s'est produit dans ExternalService? Ça dépend:
si vous pouvez gérer certains types d'exceptions, vous devez les intercepter et les traiter. Pour les autres, faites-les bouillonner.
si vous avez besoin d'un journal ou d'une réponse à l'utilisateur de l'exception spécifique, vous pouvez les attraper. Pour les autres, faites-les bouillonner.
la source
Je pense que lors de la déclaration d'exception d'application, il doit s'agir d'une exception non vérifiée, c'est-à-dire d'une sous-classe de RuntimeException. La raison en est qu'il n'encombrera pas le code d'application avec la déclaration try-catch et throws sur la méthode. Si votre application utilise Java Api, ce qui génère des exceptions vérifiées qui doivent de toute façon être gérées. Dans d'autres cas, l'application peut lever une exception non vérifiée. Si l'appelant de l'application doit toujours gérer l'exception non vérifiée, cela peut être fait.
la source
La règle que j'utilise est: ne jamais utiliser d'exceptions non contrôlées! (ou quand vous ne voyez aucun moyen de contourner cela)
Du point de vue du développeur utilisant votre bibliothèque ou de l'utilisateur final utilisant votre bibliothèque / application, il craint vraiment d'être confronté à une application qui se bloque en raison d'une exception non résolue. Et compter sur un fourre-tout n'est pas bon non plus.
De cette façon, l'utilisateur final peut toujours recevoir un message d'erreur, au lieu de la disparition complète de l'application.
la source