248 votes

Quand choisir les exceptions cochées et non cochées ?

En Java (ou tout autre langage avec des exceptions vérifiées), lorsque vous créez votre propre classe d'exception, comment décidez-vous si elle doit être vérifiée ou non ?

Mon instinct me dit qu'une exception vérifiée serait utilisée dans les cas où l'appelant pourrait être en mesure de récupérer de manière productive, alors qu'une exception non vérifiée serait plutôt utilisée dans les cas irrécupérables, mais je serais intéressé par les réflexions des autres.

14 votes

Barry Ruzek a écrit un excellent guide sur le choix des exceptions vérifiées ou non vérifiées.

0 votes

Ce lien peut vous aider à comprendre le concept même s'il est lié à nodejs joyent.com/node-js/production/design/errors

279voto

Gili Points 14674

Les exceptions vérifiées sont excellentes, à condition que vous compreniez quand elles doivent être utilisées. L'API de base de Java ne respecte pas ces règles pour les exceptions SQLException (et parfois pour les exceptions IOException), c'est pourquoi elles sont si terribles.

Exceptions vérifiées doit être utilisé pour prévisible mais non évitable les erreurs qui sont raisonnable de récupérer auprès de .

Exceptions non vérifiées doit être utilisé pour tout le reste.

Je vais vous expliquer ce que cela signifie, car la plupart des gens ne le comprennent pas.

  1. Prévisible mais non évitable : L'appelant a fait tout ce qui était en son pouvoir pour valider les paramètres d'entrée, mais une condition indépendante de sa volonté a fait échouer l'opération. Par exemple, vous essayez de lire un fichier mais quelqu'un le supprime entre le moment où vous vérifiez son existence et le moment où l'opération de lecture commence. En déclarant une exception vérifiée, vous dites à l'appelant d'anticiper cet échec.
  2. Raisonnable pour récupérer de : Il est inutile de dire aux appelants d'anticiper des exceptions qu'ils ne peuvent pas récupérer. Si un utilisateur tente de lire un fichier inexistant, l'appelant peut lui demander un nouveau nom de fichier. D'autre part, si la méthode échoue en raison d'un bogue de programmation (arguments de méthode invalides ou mise en œuvre de méthode défectueuse), l'application ne peut rien faire pour résoudre le problème en cours d'exécution. Le mieux qu'elle puisse faire est d'enregistrer le problème et d'attendre que le développeur le règle ultérieurement.

Sauf si l'exception que vous lancez répond aux critères suivants tous des conditions ci-dessus, il doit utiliser une exception non vérifiée.

Réévaluer à tous les niveaux : Parfois, la méthode qui attrape l'exception vérifiée n'est pas le bon endroit pour gérer l'erreur. Dans ce cas, considérez ce qui est raisonnable pour vos propres appelants. Si l'exception est prévisible, non évitable et qu'il est raisonnable pour eux de la récupérer, alors vous devriez lancer une exception vérifiée vous-même. Sinon, vous devez envelopper l'exception dans une exception non vérifiée. Si vous suivez cette règle, vous vous retrouverez à convertir des exceptions vérifiées en exceptions non vérifiées et vice versa, selon la couche dans laquelle vous vous trouvez.

Pour les exceptions vérifiées et non vérifiées, utiliser le bon niveau d'abstraction . Par exemple, un dépôt de code avec deux implémentations différentes (base de données et système de fichiers) devrait éviter d'exposer les détails spécifiques à l'implémentation en lançant la commande SQLException o IOException . Au lieu de cela, il doit envelopper l'exception dans une abstraction qui englobe toutes les implémentations (par ex. RepositoryException ).

2 votes

"Vous essayez de lire un fichier mais quelqu'un le supprime entre le moment où vous vérifiez s'il existe et le moment où l'opération de lecture commence." => Comment cela peut-il être 'attendu' ? Pour moi, cela ressemble plus à : Inattendu et évitable Qui s'attendrait à ce qu'un fichier soit supprimé juste entre 2 déclarations ?

11 votes

@KorayTugay Attendu ne signifie pas que le scénario est typique. Cela signifie simplement que nous pouvons prévoir à l'avance que cette erreur se produira (par rapport aux erreurs de programmation qui ne peuvent être prévues à l'avance). Imprévisible fait référence au fait que le programmeur ne peut rien faire pour empêcher l'utilisateur ou d'autres applications de supprimer un fichier entre le moment où nous vérifions son existence et le moment où l'opération de lecture commence.

0 votes

Ainsi, tout problème lié à la base de données dans la méthode doit lancer une exception vérifiée ?

62voto

Espo Points 24318

De Un apprenant de Java :

Lorsqu'une exception se produit, vous devez soit attraper et gérer l'exception, soit dire au compilateur que l'on ne peut pas la gérer en déclarant que votre méthode lève cette exception, alors le code qui utilise votre méthode devra gérer cette exception (même s'il peut aussi peut aussi choisir de déclarer qu'il lève l'exception s'il ne peut pas la gérer).

Le compilateur vérifiera que nous avons fait l'une des deux choses (catch, ou déclarer). C'est ce qu'on appelle les "Checked exceptions. Mais les erreurs et les exceptions d'exécution Les exceptions ne sont pas vérifiées par compilateur (même si vous pouvez choisir d'attraper, ou de déclarer, ce n'est pas obligatoire). Ces deux-là sont donc appelées Exceptions non vérifiées.

Les erreurs sont utilisées pour représenter les conditions qui se produisent en dehors de l'application l'application, comme une panne du système. Les exceptions d'exécution sont généralement provoquées par une erreur dans la logique de l'application. Vous ne pouvez rien faire rien dans ces situations. Lorsque exception d'exécution se produit, vous devez réécrire le code de votre programme. Ainsi, ces ne sont pas vérifiées par le compilateur. Ces exceptions d'exécution seront découvertes pendant période de développement et de test. Puis nous devons refactoriser notre code pour supprimer ces erreurs.

14 votes

C'est l'opinion orthodoxe. Mais il y a beaucoup de controverse à ce sujet.

53voto

Konrad Rudolph Points 231505

La règle que j'applique est la suivante : n'utilisez jamais d'exceptions non vérifiées ! (ou lorsque vous ne voyez aucun moyen de les contourner).

Il y a de très bons arguments pour le contraire : Ne jamais utiliser d'exceptions vérifiées. Je suis réticent à prendre parti dans le débat, mais il semble y avoir un large consensus sur le fait que l'introduction des exceptions vérifiées était une mauvaise décision a posteriori. Veuillez ne pas tirer sur le messager et vous référer à ceux arguments .

4 votes

À mon avis, les exceptions vérifiées auraient pu être un atout majeur s'il y avait eu un moyen facile pour une méthode de déclarer qu'elle ne s'attend pas à ce que les méthodes appelées dans un bloc de code particulier lèvent certaines exceptions (ou n'en lèvent aucune), et que toute exception vérifiée qui est levée contrairement à cette attente devrait être enveloppée dans un autre type d'exception et relancée. Je dirais que dans 90 % des cas, lorsque le code n'est pas préparé à faire face à une exception vérifiée, ce type d'enveloppement et de rejet serait la meilleure façon de la gérer, mais comme le langage ne le permet pas, c'est rarement le cas.

0 votes

@supercat Essentiellement, c'est ça : Je suis un fan inconditionnel de la vérification stricte des types et les exceptions vérifiées sont une extension logique de cela. J'ai complètement abandonné les exceptions vérifiées, même si je les aime beaucoup sur le plan conceptuel.

2 votes

L'un des problèmes que j'ai avec la conception des exceptions, que le mécanisme que j'ai décrit résoudrait, est que si foo est documenté comme jetant barException lors de la lecture au-delà de la fin d'un fichier, et foo appelle une méthode qui lance barException même si foo ne s'attend pas à ce qu'il le fasse, le code qui appelle foo pensera que la fin du fichier a été atteinte, et n'aura aucun indice que quelque chose d'inattendu s'est produit. Je considérerais cette situation comme étant celle où les exceptions vérifiées devrait est le plus utile, mais c'est aussi le seul cas où le compilateur autorise les exceptions vérifiées non gérées.

51voto

Stephane Points 425

Sur tout système suffisamment grand, avec de nombreuses couches, les exceptions vérifiées sont inutiles car, de toute façon, vous avez besoin d'une stratégie au niveau de l'architecture pour gérer la façon dont l'exception sera traitée (utiliser une barrière de fautes).

Avec des exceptions vérifiées, votre stratégie de gestion des erreurs est micro-gérée et c'est insupportable sur tout grand système.

La plupart du temps, vous ne savez pas si une erreur est "récupérable" parce que 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 en 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 possible de la récupérer ? Je ne sais pas, car dans sa couche, l'appelant de mon API StringToInt peut déjà avoir validé l'entrée, et si cette exception est levée, il s'agit soit d'un bug, soit d'une corruption de données, et ce n'est pas récupérable pour cette couche.

Dans ce cas, l'appelant de l'API ne veut pas attraper l'exception. Il veut seulement laisser l'exception "remonter". Si je choisis une exception vérifiée, cet appelant aura beaucoup de blocs de capture inutiles uniquement pour relancer artificiellement l'exception.

Ce qui est récupérable dépend la plupart du temps de l'appelant de l'API, et non de l'écrivain de l'API. Une API ne devrait pas utiliser d'exceptions vérifiées, car seules les exceptions non vérifiées permettent de choisir entre attraper ou ignorer une exception.

3 votes

C'est très proche de userstories.blogspot.com/2008/12/

17 votes

@alexsmail Internet ne manque jamais de me surprendre, en fait c'est mon blog :)

32voto

OscarRyz Points 82553

Vous avez raison.

Exceptions non vérifiées sont utilisés pour laisser le système faire des erreurs rapides ce qui est une bonne chose. Vous devez indiquer clairement ce que votre méthode attend pour fonctionner correctement. De cette façon, vous pouvez valider l'entrée une seule fois.

Par exemple :

/**
 * @params operation - The operation to execute.
 * @throws IllegalArgumentException if the operation is "exit"
 */
 public final void execute( String operation ) {
     if( "exit".equals(operation)){
          throw new IllegalArgumentException("I told you not to...");
     }
     this.operation = operation; 
     .....  
 }
 private void secretCode(){
      // we perform the operation.
      // at this point the opreation was validated already.
      // so we don't worry that operation is "exit"
      .....  
 }

Pour donner un exemple. Le fait est que, si le système échoue rapidement, vous saurez où et pourquoi il a échoué. Vous obtiendrez une trace de pile comme :

 IllegalArgumentException: I told you not to use "exit" 
 at some.package.AClass.execute(Aclass.java:5)
 at otherPackage.Otherlass.delegateTheWork(OtherClass.java:4569)
 ar ......

Et vous saurez ce qui s'est passé. L'autre classe dans la méthode "delegateTheWork" (à la ligne 4569) a appelé votre classe avec la valeur "exit", même si elle ne devrait pas le faire, etc.

Sinon, vous devrez saupoudrer votre code de validations, ce qui est source d'erreurs. De plus, il est parfois difficile de savoir ce qui s'est mal passé et il faut s'attendre à des heures de débogage frustrantes.

La même chose se produit avec les exceptions NullPointerExceptions. Si vous avez une classe de 700 lignes avec 15 méthodes, qui utilise 30 attributs et qu'aucun d'entre eux ne peut être nul, au lieu de valider la nullité dans chacune de ces méthodes, vous pouvez rendre tous ces attributs en lecture seule et les valider dans le constructeur ou la méthode de fabrication.

 public static MyClass createInstane( Object data1, Object data2 /* etc */ ){ 
      if( data1 == null ){ throw NullPointerException( "data1 cannot be null"); }

  }

  // the rest of the methods don't validate data1 anymore.
  public void method1(){ // don't worry, nothing is null 
      ....
  }
  public void method2(){ // don't worry, nothing is null 
      ....
  }
  public void method3(){ // don't worry, nothing is null 
      ....
  }

Exceptions vérifiées Ils sont utiles lorsque le programmeur (vous ou vos collègues) a fait tout ce qu'il fallait, validé l'entrée, exécuté des tests et que le code est parfait, mais qu'il se connecte à un service web tiers qui peut être hors service (ou qu'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 des données, quelque chose a mal tourné.

Dans ce cas, il n'y a rien que vous ou vos collègues puissiez faire pour l'aider. Mais vous devez quand même 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 vous traitez l'exception, que pouvez-vous faire lorsque cela se produit ? la plupart du temps, vous essayez simplement d'enregistrer l'erreur, probablement de sauvegarder votre travail (le travail de l'application) et de présenter un message à l'utilisateur. ( Le site blabla est en panne, veuillez réessayer plus tard, etc.)

Si les exceptions vérifiées sont surutilisées ( en ajoutant "throw Exception" dans la signature de toutes les méthodes ), alors votre code deviendra très fragile, car tout le monde ignorera cette exception ( parce qu'elle est trop générale ) et la qualité du code sera sérieusement compromise.

Si vous abusez des exceptions non vérifiées, quelque chose de similaire se produira. Les utilisateurs de ce code ne savent pas si quelque chose peut mal tourner et beaucoup de try{...}catch( Throwable t ) vont apparaître.

2 votes

Bien dit ! +1. Je suis toujours surpris que cette distinction appelant(non coché)/appelé(coché) ne soit pas plus évidente...

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