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 !

19voto

David Lichteblau Points 2973

En bref :

Les exceptions sont une question de conception de l'API. -- Ni plus, ni moins.

L'argument pour les exceptions vérifiées :

Pour comprendre pourquoi les exceptions vérifiées peuvent ne pas être une bonne chose, retournons la question et demandons : quand ou pourquoi les exceptions vérifiées sont-elles intéressantes, c'est-à-dire pourquoi voulez-vous que le compilateur impose la déclaration des exceptions ?

La réponse est évidente : parfois, vous besoin de pour attraper une exception, et cela n'est possible que si le code appelé offre une classe d'exception spécifique pour l'erreur qui vous intéresse.

Par conséquent, l'argument pour des exceptions vérifiées est que le compilateur oblige les programmeurs à déclarer quelles exceptions sont lancées, et Avec un peu de chance, le programmeur documentera alors également les classes d'exceptions spécifiques et les erreurs qui les provoquent.

En réalité, il arrive trop souvent qu'un paquet com.acme ne lance qu'un AcmeException plutôt que des sous-classes spécifiques. Les appelants doivent alors gérer, déclarer ou signaler de nouveau AcmeExceptions mais ne peut toujours pas être certain qu'une AcmeFileNotFoundError s'est produit ou un AcmePermissionDeniedError .

Donc si vous êtes seulement intéressé par un AcmeFileNotFoundError la solution est de déposer une demande de fonctionnalité auprès des programmeurs d'ACME et de leur demander d'implémenter, de déclarer et de documenter cette sous-classe de AcmeException .

Alors pourquoi s'embêter ?

Par conséquent, même avec des exceptions vérifiées, le compilateur ne peut pas forcer les programmeurs à jeter utile exceptions. Il s'agit toujours d'une question de qualité de l'API.

Par conséquent, les langages sans exceptions vérifiées ne s'en sortent généralement pas beaucoup plus mal. Les programmeurs peuvent être tentés de lancer des instances non spécifiques d'une exception générale. Error plutôt qu'une classe AcmeException mais s'ils se soucient un tant soit peu de la qualité de leur API, ils apprendront à introduire une méthode d'évaluation de la qualité de l'API. AcmeFileNotFoundError après tout.

Globalement, la spécification et la documentation des exceptions ne sont pas très différentes de la spécification et de la documentation des méthodes ordinaires, par exemple. Il s'agit là aussi d'une question de conception de l'API, et si un programmeur a oublié d'implémenter ou d'exporter une fonctionnalité utile, l'API doit être améliorée pour que vous puissiez travailler utilement avec elle.

Si vous suivez ce raisonnement, il devrait être évident que les "tracas" de la déclaration, de la capture et du rejet des exceptions, si courants dans des langages comme Java, n'apportent souvent que peu de valeur ajoutée.

Il est également intéressant de noter que la VM de Java ne no ont des exceptions vérifiées -- seul le compilateur Java les vérifie, et les fichiers de classe dont les déclarations d'exceptions ont été modifiées sont compatibles au moment de l'exécution. La sécurité de la VM Java n'est pas améliorée par les exceptions vérifiées, mais uniquement par le style de codage.

16voto

Virtlink Points 12475

Catégories d'exceptions

Lorsque je parle d'exceptions, je me réfère toujours à Les exceptions qui dérangent d'Eric Lippert article de blog. Il classe les exceptions dans les catégories suivantes :

  • Fatal - Ces exceptions sont pas de votre faute Vous ne pouvez pas les prévenir, et vous ne pouvez pas les gérer de manière raisonnable. Par exemple, OutOfMemoryError ou ThreadAbortException .
  • Boneheaded - Ces exceptions sont de votre faute : vous auriez dû les éviter, et ils représentent des bogues dans votre code. Par exemple, ArrayIndexOutOfBoundsException , NullPointerException ou tout IllegalArgumentException .
  • Vexant - Ces exceptions sont non exceptionnel Ce n'est pas de votre faute, vous ne pouvez pas les empêcher, mais vous devrez y faire face. Ils sont souvent le résultat d'une décision de conception malheureuse, comme le fait de jeter NumberFormatException de Integer.parseInt au lieu de fournir un Integer.tryParseInt qui renvoie un booléen false en cas d'échec de l'analyse.
  • Exogène - Ces exceptions sont généralement exceptionnels ne sont pas de votre faute, vous ne pouvez pas (raisonnablement) les empêcher, mais vous devez les manipuler . Par exemple, FileNotFoundException .

Un utilisateur de l'API :

  • ne doit pas poignée fatal ou imbécile exceptions.
  • devrait poignée vexant exceptions, mais elles ne devraient pas se produire dans une API idéale.
  • doit poignée exogène exceptions.

Exceptions vérifiées

Le fait que l'utilisateur de l'API doit Le traitement d'une exception particulière fait partie du contrat de la méthode entre l'appelant et l'appelé. Le contrat spécifie, entre autres, le nombre et le type d'arguments attendus par le destinataire, le type de valeur de retour que l'appelant peut attendre, et le type d'exception à traiter. les exceptions que l'appelant doit gérer .

Depuis vexant Les exceptions ne devraient pas exister dans une API, seules ces exogène les exceptions doivent être exceptions vérifiées pour faire partie du contrat de la méthode. Les exceptions sont relativement rares exogène Toute API devrait donc comporter relativement peu d'exceptions vérifiées.

Une exception vérifiée est une exception qui doivent être traitées . Gérer une exception peut être aussi simple que de l'avaler. Là ! L'exception est gérée. Point final. Si le développeur veut la gérer de cette façon, très bien. Mais il ne peut pas ignorer l'exception, et il a été averti.

Problèmes d'API

Mais toute API qui a vérifié vexant y fatal Les exceptions (par exemple, la JCL) exerceront une pression inutile sur les utilisateurs de l'API. De telles exceptions ont à traiter, mais soit l'exception est si courante qu'elle n'aurait pas dû être une exception en premier lieu, soit rien ne peut être fait lors de son traitement. Et este fait que les développeurs Java détestent les exceptions vérifiées.

De plus, de nombreuses API ne disposent pas d'une hiérarchie de classes d'exception appropriée, ce qui fait que toutes sortes de causes d'exception non homogènes sont représentées par une seule classe d'exception vérifiée (par ex. IOException ). Et c'est aussi la raison pour laquelle les développeurs Java détestent les exceptions vérifiées.

Conclusion

Exogène les exceptions sont celles qui ne sont pas de votre faute, qui n'auraient pas pu être évitées et qui doivent être traitées. Elles constituent un petit sous-ensemble de toutes les exceptions qui peuvent être levées. Les API ne devraient avoir que des vérifié exogène exceptions et toutes les autres exceptions non vérifiées. Cela permettra d'améliorer les API, d'alléger la tâche de l'utilisateur de l'API et, par conséquent, de réduire la nécessité d'attraper toutes les exceptions non vérifiées, de les avaler ou de les relancer.

Ne détestez donc pas Java et ses exceptions vérifiées. Détestez plutôt les API qui surutilisent les exceptions vérifiées.

8voto

Newtopian Points 3335

Ok... Les exceptions vérifiées ne sont pas idéales et ont quelques réserves, mais elles ont leur utilité. Lors de la création d'une API, il existe des cas spécifiques d'échecs qui sont contractuels de cette API. Dans le contexte d'un langage fortement typé statiquement tel que Java, si l'on n'utilise pas d'exceptions vérifiées, on doit s'appuyer sur une documentation et des conventions ad hoc pour transmettre la possibilité d'une erreur. En agissant ainsi, on supprime tous les avantages que le compilateur peut apporter dans la gestion des erreurs et on s'en remet entièrement à la bonne volonté des programmeurs.

Ainsi, si l'on supprime l'exception vérifiée, comme cela a été fait en C#, comment peut-on alors transmettre de manière programmatique et structurelle la possibilité d'une erreur ? Comment informer le code client que telle ou telle erreur peut se produire et doit être traitée ?

J'entends toutes sortes d'horreurs lorsqu'il s'agit d'exceptions vérifiées, elles sont mal utilisées, c'est certain, mais les exceptions non vérifiées le sont aussi. Attendez quelques années, lorsque les API seront empilées en couches profondes, et vous supplierez le retour d'une sorte de moyen structuré pour transmettre les échecs.

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).

Beaucoup ont affirmé que le fait de ne pas pouvoir charger le fichier était presque toujours la fin du monde pour le processus et qu'il devait mourir d'une mort horrible et douloureuse. Alors oui... bien sûr... ok... vous construisez une API pour quelque chose et elle charge un fichier à un moment donné... Moi, en tant qu'utilisateur de cette API, je ne peux que répondre... "Qui êtes-vous pour décider quand mon programme doit se planter !" Bien sûr, si j'avais le choix entre les exceptions qui sont absorbées et ne laissent aucune trace et l'EletroFlabbingChunkFluxManifoldChuggingException avec une trace de pile plus profonde que la tranchée de Marianna, je prendrais la seconde sans une once d'hésitation, mais cela signifie-t-il que c'est la façon souhaitable de traiter les exceptions ? Ne pouvons-nous pas être quelque part au milieu, où l'exception serait refondue et enveloppée chaque fois qu'elle traverse un nouveau niveau d'abstraction afin qu'elle signifie réellement quelque chose ?

Enfin, la plupart des arguments que je vois sont "Je ne veux pas m'occuper des exceptions, beaucoup de gens ne veulent pas s'occuper des exceptions. Les exceptions vérifiées m'obligent à les gérer, donc je déteste les exceptions vérifiées". Éliminer complètement un tel mécanisme et le reléguer dans le gouffre de l'enfer des goto est tout simplement stupide et manque de jugement et de vision.

Si nous éliminons l'exception vérifiée, nous pourrions également éliminer le type de retour des fonctions et toujours retourner une variable "anytype"... Cela rendrait la vie tellement plus simple, n'est-ce pas ?

8voto

Boann Points 11904

Ce n'est pas un argument contre le concept pur des exceptions vérifiées, mais la hiérarchie des classes que Java utilise pour elles est une véritable foire d'empoigne. Nous appelons toujours ces choses simplement "exceptions" - ce qui est correct, car les spécifications du langage les appelle aussi comme ça - mais comment une exception est-elle nommée et représentée dans le système de types ?

Par la classe Exception qu'on imagine ? Eh bien non, parce que Exception sont des exceptions, et de même, les exceptions sont Exception sauf pour les exceptions qui sont no Exception car les autres exceptions sont en fait Error s, qui sont l'autre type d'exception, une sorte d'exception extra-exceptionnelle qui ne devrait jamais se produire, sauf si elle se produit, et que vous ne devriez jamais attraper, sauf si vous y êtes obligé. Mais ce n'est pas tout, car vous pouvez également définir d'autres exceptions qui ne sont ni des Exception s ni Error mais simplement Throwable exceptions.

Lesquelles de ces exceptions sont les exceptions "vérifiées" ? Throwable sont des exceptions vérifiées, sauf si elles sont également Error qui sont des exceptions non vérifiées, et puis il y a les Exception qui sont également Throwable et sont le principal type d'exception vérifiée, sauf qu'il y a une exception à cela aussi, qui est que si elles sont aussi des RuntimeException car c'est l'autre type d'exception non vérifiée.

Quels sont les RuntimeException s pour ? Eh bien, comme son nom l'indique, il s'agit d'exceptions, comme toutes les autres exceptions. Exception et elles se produisent au moment de l'exécution, comme toutes les exceptions en fait, sauf que RuntimeException sont exceptionnels par rapport aux autres temps d'exécution Exception parce qu'ils ne sont pas censés se produire, sauf si vous faites une erreur stupide, bien que RuntimeException ne sont jamais Error s, donc ils sont pour les choses qui sont exceptionnellement erronées mais qui ne sont pas vraiment Error s. Sauf pour RuntimeErrorException qui est en fait un RuntimeException para Error s. Mais toutes les exceptions ne sont-elles pas censées représenter des circonstances erronées de toute façon ? Oui, toutes. Sauf pour ThreadDeath En effet, la documentation explique qu'il s'agit d'une "occurrence normale" et que c'est pour cette raison que l'on en a fait un type d'exception. Error .

De toute façon, puisque nous divisons toutes les exceptions par le milieu en Error (qui sont destinés à des exceptions d'exécution exceptionnelles, donc non vérifiés) et les Exception (qui sont pour les erreurs d'exécution moins exceptionnelles, donc vérifiées sauf quand elles ne le sont pas), nous avons maintenant besoin de deux types différents de chacune de plusieurs exceptions. Nous avons donc besoin de IllegalAccessError y IllegalAccessException y InstantiationError y InstantiationException y NoSuchFieldError y NoSuchFieldException y NoSuchMethodError y NoSuchMethodException y ZipError y ZipException .

Sauf que même lorsqu'une exception est vérifiée, il existe toujours des moyens (assez faciles) de tromper le compilateur et de la lancer sans qu'elle soit vérifiée. Si vous le faites, vous pouvez obtenir un UndeclaredThrowableException sauf dans d'autres cas, où il pourrait apparaître comme un UnexpectedException ou un UnknownException (ce qui n'a rien à voir avec UnknownError qui ne concerne que les "exceptions sérieuses"), ou encore une ExecutionException ou un InvocationTargetException ou un ExceptionInInitializerError .

Oh, et nous ne devons pas oublier la nouvelle version de Java 8. UncheckedIOException qui est un RuntimeException exception conçue pour vous permettre de jeter le concept de vérification d'exception par la fenêtre en enveloppant les objets vérifiés dans une enveloppe. IOException les exceptions causées par des erreurs d'E/S (qui ne provoquent pas de IOError exceptions, bien que cela existe aussi) qui sont exceptionnellement difficiles à gérer et que vous avez donc besoin qu'elles ne soient pas vérifiées.

Merci, Java !

6voto

Kurt Schelfthout Points 5437

En effet, les exceptions vérifiées augmentent d'une part la robustesse et la correction de votre programme (vous êtes obligé de faire des déclarations correctes de vos interfaces - les exceptions lancées par une méthode sont fondamentalement un type de retour spécial). D'autre part, vous êtes confronté au problème suivant : comme les exceptions "remontent", vous devez très souvent modifier un grand nombre de méthodes (tous les appelants, les appelants des appelants, etc.) lorsque vous modifiez les exceptions lancées par une méthode.

Les exceptions vérifiées en Java ne résolvent pas ce dernier problème ; C# et VB.NET jettent le bébé avec l'eau du bain.

Une approche intéressante qui prend la voie du milieu est décrite dans le document suivant ce document OOPSLA 2005 (ou le rapport technique correspondant .)

En bref, il vous permet de dire : method g(x) throws like f(x) ce qui signifie que g lève toutes les exceptions levées par f. Voilà, des exceptions vérifiées sans le problème des modifications en cascade.

Bien qu'il s'agisse d'un article universitaire, je vous encourage à le lire (en partie), car il explique bien les avantages et les inconvénients des exceptions vérifiées.

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