499 votes

Les arguments contre les exceptions vérifiées

Depuis plusieurs années, je ne parviens pas à obtenir une réponse décente à la question suivante : pourquoi certains développeurs sont-ils si opposés aux exceptions vérifiées ? J'ai eu de nombreuses conversations, j'ai lu des choses sur des blogs, j'ai lu ce que Bruce Eckel avait à dire (la première personne que j'ai vue s'exprimer contre elles).

Je suis en train d'écrire un nouveau code et je fais très attention à la façon dont je traite les exceptions. J'essaie de comprendre le point de vue de la foule "nous n'aimons pas les exceptions vérifiées" et je n'y arrive toujours pas.

Toutes les conversations que j'ai se terminent par la même question qui reste sans réponse... Laissez-moi vous expliquer :

En général (d'après la façon dont Java a été conçu),

  • Error est pour les choses qui ne devraient jamais être prises (VM est allergique aux cacahuètes et quelqu'un a fait tomber un pot de cacahuètes sur lui)
  • RuntimeException est pour les choses que le programmeur a mal faites (le programmeur a oublié la fin d'un tableau).
  • Exception (sauf RuntimeException ) est pour les choses qui sont hors du contrôle du programmeur (le disque se remplit lors de l'écriture dans le système de fichiers, la limite du gestionnaire de fichiers pour le processus a été atteinte et vous ne pouvez plus ouvrir de fichiers).
  • Throwable est simplement le parent de tous les types d'exception.

Un argument courant que j'entends est que si une exception se produit, tout ce que le développeur va faire est de quitter le programme.

Un autre argument courant que j'entends est que les exceptions vérifiées rendent plus difficile le remaniement du code.

Pour l'argument "tout ce que je vais faire est de quitter", je dis que même si vous quittez, vous devez afficher un message d'erreur raisonnable. Si vous vous contentez de gérer les erreurs, vos utilisateurs ne seront pas très heureux lorsque le programme quittera sans indication claire de la raison.

Pour ceux qui pensent que "cela rend difficile le remaniement", cela indique que le niveau d'abstraction approprié n'a pas été choisi. Plutôt que de déclarer qu'une méthode lance un IOException le IOException devrait être transformée en une exception plus adaptée à ce qui se passe.

Je n'ai pas de problème à envelopper Main avec catch(Exception) (ou dans certains cas catch(Throwable) afin de s'assurer que le programme peut se terminer de manière élégante - mais j'attrape toujours les exceptions spécifiques dont j'ai besoin. Cela me permet, au minimum, d'afficher un message d'erreur approprié.

La question à laquelle les gens ne répondent jamais est la suivante :

Si vous lancez RuntimeException au lieu de Exception des sous-classes, alors comment savoir ce que vous êtes censé attraper ?

Si la réponse est catch Exception alors vous traitez les erreurs du programmeur de la même manière que les exceptions du système. Cela me semble erroné.

Si vous attrapez Throwable alors vous traitez les exceptions système et les erreurs VM (et autres) de la même manière. Cela me semble erroné.

Si la réponse est que vous n'attrapez que les exceptions que vous savez être levées, comment savez-vous lesquelles ? Que se passe-t-il lorsque le programmeur X lance une nouvelle exception et oublie de l'attraper ? Cela me semble très dangereux.

Je dirais qu'un programme qui affiche une trace de la pile est mauvais. Les personnes qui n'aiment pas les exceptions vérifiées ne sont-elles pas de cet avis ?

Donc, si vous n'aimez pas les exceptions contrôlées, pouvez-vous expliquer pourquoi et répondre à la question qui n'a pas reçu de réponse ?

Je ne cherche pas à savoir quand utiliser l'un ou l'autre modèle, ce que je cherche c'est pourquoi les personnes s'étendent de RuntimeException parce qu'ils n'aiment pas s'étendre de Exception et/ou pourquoi ils attrapent une exception et relancent ensuite une RuntimeException plutôt que d'ajouter des lancers à leur méthode. Je veux comprendre les raisons pour lesquelles on n'aime pas les exceptions vérifiées.

47 votes

Je ne pense pas que ce soit complètement subjectif - c'est une fonctionnalité de la langue qui a été conçue pour avoir une utilisation spécifique, plutôt que pour que chacun puisse décider de ce à quoi elle sert pour lui-même. Et ce n'est pas spécialement argumentatif, ça répond à l'avance à des réfutations spécifiques que les gens auraient pu facilement trouver.

7 votes

Allez. Considéré comme une caractéristique de la langue, ce sujet a été et peut être abordé de manière objective.

6 votes

@cletus "répondre à votre propre question" si j'avais la réponse je n'aurais pas posé la question !

6voto

Piotr Sobczyk Points 1539

Le problème

Le pire problème que je vois avec le mécanisme de gestion des exceptions est que il introduit la duplication du code à grande échelle ! Soyons honnêtes : dans la plupart des projets, dans 95 % des cas, tout ce que les développeurs ont besoin de faire avec une exception est de la communiquer d'une manière ou d'une autre à l'utilisateur (et, dans certains cas, à l'équipe de développement également, par exemple en envoyant un e-mail avec la trace de la pile). Ainsi, la même ligne ou le même bloc de code est utilisé à chaque fois que l'exception est traitée.

Supposons que nous procédions à une simple journalisation dans chaque bloc catch pour un certain type d'exception vérifiée :

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

S'il s'agit d'une exception courante, il peut même y avoir plusieurs centaines de blocs try-catch de ce type dans une base de code plus importante. Supposons maintenant que nous ayons besoin d'introduire un traitement des exceptions basé sur une boîte de dialogue popup au lieu d'une journalisation en console ou que nous commencions à envoyer un e-mail à l'équipe de développement.

Attendez un instant... allons-nous vraiment modifier tout cela à plusieurs centaines d'endroits dans le code ? ! Vous comprenez ce que je veux dire :-).

La solution

Ce que nous avons fait pour résoudre ce problème a été d'introduire le concept de les gestionnaires d'exception (que j'appellerai plus loin les EH) pour centraliser le traitement des exceptions. Pour chaque classe qui a besoin de gérer les exceptions, une instance du gestionnaire d'exceptions est injectée par notre programme de gestion des exceptions. Injection de dépendances cadre. Ainsi, le modèle typique de traitement des exceptions ressemble maintenant à ceci :

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

Maintenant, pour personnaliser le traitement des exceptions, il suffit de modifier le code à un seul endroit (code EH).

Bien sûr, pour des cas plus complexes, nous pouvons mettre en œuvre plusieurs sous-classes d'EH et tirer parti des fonctionnalités que notre cadre DI nous fournit. En modifiant la configuration de notre cadre DI, nous pouvons facilement changer l'implémentation des EH de manière globale ou fournir des implémentations spécifiques des EH aux classes ayant des besoins particuliers en matière de gestion des exceptions (par exemple en utilisant l'annotation @Named de Guice).

De cette façon, nous pouvons différencier le comportement de traitement des exceptions dans la version de développement et la version de production de l'application (par exemple, développement - enregistrement de l'erreur et arrêt de l'application, production - enregistrement de l'erreur avec plus de détails et poursuite de l'exécution de l'application) sans effort.

Une dernière chose

Enfin, il peut sembler que le même type de centralisation peut être obtenu en faisant simplement passer nos exceptions "vers le haut" jusqu'à ce qu'elles arrivent à une classe de traitement des exceptions de niveau supérieur. Mais cela conduit à un encombrement du code et des signatures de nos méthodes et introduit des problèmes de maintenance mentionnés par d'autres dans ce fil.

6voto

Piotr Sobczyk Points 1539

Cet article est le meilleur texte sur la gestion des exceptions en Java que j'ai jamais lu.

Il privilégie les exceptions non vérifiées par rapport aux exceptions vérifiées, mais ce choix est expliqué de manière très détaillée et repose sur des arguments solides.

Je ne veux pas trop citer le contenu de l'article ici (il est préférable de le lire dans son ensemble) mais il couvre la plupart des arguments des défenseurs des exceptions non contrôlées de ce fil. En particulier, cet argument (qui semble être assez populaire) est couvert :

Prenons le cas où l'exception a été lancée quelque part au bas des couches de l'API et qu'elle a simplement remonté parce que personne ne savait qu'il était possible que cette erreur se produise, et ce même s'il s'agissait d'un type d'erreur très plausible lorsque le code appelant l'a lancée (FileNotFoundException par exemple, par opposition à VogonsTrashingEarthExcept... auquel cas il n'y aurait aucune importance que nous la traitions ou non puisqu'il n'y a plus rien pour la traiter).

L'auteur "réponses" :

Il est absolument incorrect de supposer que toutes les exceptions d'exécution ne doivent pas être capturées et propagées jusqu'au "sommet" de l'application. l'application. (...) Pour chaque condition exceptionnelle devant être être traitée de façon distincte - par les exigences du système/de l'entreprise -, les les programmeurs doivent décider où la capturer et ce qu'il faut faire une fois que la condition est détectée. Cela doit être fait en respectant strictement l besoins réels de l'application, et non en fonction d'une alerte du compilateur. Tous les sites toutes les autres erreurs doivent pouvoir se propager librement jusqu'à la gestionnaire le plus élevé, où elles seront enregistrées et où une action gracieuse (peut-être, une action gracieuse (peut-être la fin) sera prise.

Et la pensée principale ou l'article est :

Lorsqu'il s'agit de la gestion des erreurs dans un logiciel, la seule hypothèse sûre et correcte qui puisse être faite est qu'une défaillance peut se produire dans absolument tous les sous-programmes ou modules qui existent !

Donc si " Personne ne savait que cette erreur était possible. "il y a un problème avec ce projet. Une telle exception devrait être traitée au moins par le gestionnaire d'exception le plus générique (par exemple, celui qui traite toutes les exceptions qui ne sont pas traitées par des gestionnaires plus spécifiques) comme le suggère l'auteur.

C'est triste que peu de gens semblent découvrir cet excellent article :-(. Je recommande de tout cœur à tous ceux qui hésitent à choisir la meilleure approche de prendre le temps de le lire.

6voto

Vlasec Points 372

Une chose importante que personne n'a mentionnée est l'interférence avec les interfaces et les expressions lambda.

Disons que vous définissez un MyAppException extends Exception . Il s'agit de l'exception de niveau supérieur héritée par toutes les exceptions lancées par votre application. Dans certains cas, vous ne voulez pas réagir aux exceptions particulières, vous voulez que l'appelant les résolve, donc vous déclarez throws MyAppException .

Tout semble correct jusqu'à ce que vous vouliez utiliser l'interface de quelqu'un d'autre. Évidemment, ils ne déclarent pas leur intention de lancer MyAppException donc le compilateur ne vous permet même pas d'appeler vos méthodes qui déclarent throws MyAppException là-dedans. C'est particulièrement douloureux avec java.util.function .

Cependant, si votre exception s'étend RuntimeException il n'y aura aucun problème avec les interfaces. Vous pouvez mentionner l'exception dans la JavaDoc si vous le souhaitez. Mais à part cela, il ne fait que passer silencieusement à travers n'importe quoi. Bien sûr, cela signifie qu'il peut mettre fin à votre application. Mais dans de nombreux logiciels d'entreprise, vous disposez d'une couche de gestion des exceptions et les exceptions non vérifiées vous épargnent de nombreux problèmes.

5voto

Martin Points 787

Comme on l'a déjà dit, les exceptions vérifiées n'existent pas dans le bytecode Java. Elles sont simplement un mécanisme du compilateur, un peu comme les autres contrôles syntaxiques. Je vois les exceptions vérifiées de la même façon que je vois le compilateur se plaindre d'une conditionnelle redondante : if(true) { a; } b; . C'est utile, mais je l'ai peut-être fait exprès, alors laissez-moi ignorer vos avertissements.

Le fait est que vous ne pourrez pas forcer chaque programmeur à "faire ce qu'il faut" si vous appliquez des exceptions contrôlées et que tous les autres sont maintenant des dommages collatéraux qui vous détestent pour la règle que vous avez établie.

Corrigez les mauvais programmes qui existent ! N'essayez pas d'arranger la langue pour qu'elle ne les autorise pas ! Pour la plupart des gens, "faire quelque chose à propos d'une exception" signifie simplement en informer l'utilisateur. Je peux tout aussi bien informer l'utilisateur d'une exception non vérifiée, alors gardez vos classes d'exceptions vérifiées en dehors de mon API.

5voto

Dave Elton Points 28

Pour tenter de répondre uniquement à la question sans réponse :

Si vous lancez des sous-classes RuntimeException au lieu de sous-classes Exception, comment savez-vous ce que vous êtes censé attraper ?

La question contient un raisonnement spécieux, à mon avis. Ce n'est pas parce que l'API vous dit ce qu'elle lève que vous devez le traiter de la même manière dans tous les cas. En d'autres termes, les exceptions que vous devez attraper varient en fonction du contexte dans lequel vous utilisez le composant qui lève l'exception.

Par exemple :

Si j'écris un testeur de connexion pour une base de données, ou quelque chose pour vérifier la validité d'un XPath saisi par l'utilisateur, alors je voudrais probablement attraper et rapporter toutes les exceptions vérifiées et non vérifiées qui sont lancées par l'opération.

Toutefois, si j'écris un moteur de traitement, je traiterai probablement une XPathException (vérifiée) de la même manière qu'un NPE : je la laisserai s'exécuter jusqu'au sommet du fil de travail, je sauterai le reste du lot, j'enregistrerai le problème (ou je l'enverrai à un service d'assistance pour diagnostic) et je laisserai un commentaire à l'utilisateur pour qu'il contacte l'assistance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X