web-dev-qa-db-fra.com

Capturer les objets jetables et gérer des exceptions spécifiques

Ok je sais que jetter est pas une bonne idée:

    try {
         // Some code
    } catch(Throwable e) { // Not cool!
        // handle the exception
    }

Mais récemment, je lisais un code à source ouverte et j’ai vu cet élément de code intéressant (du moins pour moi):

    try {
        // Some Code
    } catch (Throwable ex){
        response = handleException(ex, resource);
    }

    private handleException(Throwable t, String resource) {
        if (t instanceof SQLEXception) {
               // Some code
        } else if (t instanceof IllegalArgumentException) {
               //some code
        } //so on and so forth
    }

Cela ne semble pas être si mauvais? Quel est le problème avec cette approche?

22
rhel.user

Il y a diverses raisons pour lesquelles vous ne devriez pas attraper un Throwable. Tout d’abord, Throwable inclut Errors - et normalement, une application ne peut pas faire grand chose si l’une d’elles apparaît. Throwable réduit également vos chances de découvrir ce qui est arrivé. Tout ce que vous obtenez est "quelque chose de grave est arrivé" - ce qui pourrait être une catastrophe ou juste une nuisance. 

L'autre approche est meilleure, mais bien sûr, je ne voudrais toujours pas attraper Throwable, mais essayer d'attraper des exceptions plus spécifiques, si possible. Sinon, vous attrapez tout et essayez ensuite de déterminer le type de problème qui s'est produit. Votre exemple pourrait être écrit comme ...

try {
    ...
} catch (SQLEXception ex){
    response = ... ;
} catch (IllegalArgumentException ex){
    response = ...;
}

... ce qui réduirait le nombre de blocs if ( ... instanceof ... ) (nécessaires uniquement parce que l'auteur a d'abord décidé de tout prendre dans le même seau). En réalité, il s’agit de throws Throwable, alors vous n’avez évidemment pas beaucoup de choix. 

19
Florian Schaetz

Vous avez raison de dire qu'attraper Throwable n'est pas une bonne idée. Cependant, le code que vous présentez dans votre question n'attrape pas Throwable de manière perverse, mais parlons-en plus tard. Pour le moment, le code que vous présentez dans votre question présente plusieurs avantages:

1. Lisibilité

Si vous examinez attentivement le code, vous remarquerez que même si le bloc catch intercepte une variable Throwable, la méthode handleException vérifie le type d'exception levée et effectue éventuellement différentes actions en fonction de ce type.

Le code présenté dans votre question est synonyme de:

try {
    doSomething();
} catch (SQLEXception ex){
    response = handleException(resource);
} catch(IllegalArgumentException ex) {
    response = handleException(resource);
} catch(Throwable ex) {
    response = handleException(resource);
}

Même si vous devez capturer plus de 10 exceptions seulement, ce code peut facilement prendre beaucoup de lignes de code et la construction multi-catch ne va pas rendre le code plus propre. Le code que vous présentez dans votre question consiste simplement à déléguer la catch à une autre méthode afin de rendre la méthode réelle qui effectue le travail plus lisible. 

2. Réutilisation

Le code de la méthode handleRequest peut facilement être modifié et placé dans une classe d'utilitaire, puis utilisé dans l'ensemble de votre application pour gérer à la fois Exceptions et Errors. Vous pouvez même extraire la méthode en deux méthodes private; L'une qui gère Exception et l'autre qui gère Error et qui ont la méthode handleException qui prend un Throwable déléguer davantage les appels à ces méthodes. 

3. Maintien

Si vous décidez de modifier la manière dont vous enregistrez une SQLExceptions dans votre application, vous devez effectuer cette modification à un seul endroit plutôt que de consulter chaque méthode de chaque classe qui lance une SQLException.

Donc, attraper Throwable est-il une mauvaise idée? 

Le code que vous présentez dans votre question n’est pas vraiment identique à attraper Throwable seul. Le code suivant est un gros no-no: 

try {
   doSomething();
} catch(Throwable e) {
    //log, rethrow or take some action
}

Vous devriez attraper Throwable ou Exception aussi loin que possible dans la chaîne catch.

Dernier point mais non le moindre, rappelez-vous que le code que vous présentez dans votre question est le code du framework et que le framework peut encore récupérer certaines erreurs. Voir Quand attraper Java.lang.Error pour une meilleure explication.

18
CKing

Attraper Throwables de la paresse est une mauvaise idée.

C'était particulièrement tentant avant que try-multi-catch ne soit introduit.

try {
   ...
} catch (SomeException e) {
   //do something
} catch (OtherException e) {
   //do the same thing
} ...

Répéter les blocs de capture est fastidieux et prolixe, aussi certaines personnes ont-elles décidé d'attraper simplement Exception ou Throwable et de s'en passer. C'est ce qu'il faut éviter car:

  1. Il est difficile de suivre ce que vous essayez de faire.
  2. Vous pouvez finir par attraper beaucoup de choses que vous ne pouvez pas gérer. 
  3. Vous méritez une punition supplémentaire si vous avalez complètement la Throwable dans le bloc collé. (Et nous avons tous vu le code qui fait ça ... :))

Mais attraper Throwables quand il est absolument nécessaire va bien.

Quand est-ce nécessaire? Très rarement. Dans le code de type framework, il existe différents scénarios (le chargement dynamique d'une classe externe est le plus évident). Dans une application autonome, un exemple typique consiste à afficher/consigner un type de message d'erreur avant de quitter. (Gardant à l'esprit que la tentative peut échouer, vous ne voulez donc rien y mettre de critique.)

En règle générale, si vous ne pouvez rien faire à propos d'une exception/erreur, vous ne devriez pas l'attraper du tout.

9
biziclop

Il existe exactement deux utilisations valables pour utiliser un réseau énorme:

  • Si vous voulez tout gérer de manière uniforme, comme une capture de niveau supérieur pour la journalisation/la création de rapports, éventuellement suivie d'une sortie immédiate.

  • Pour réduire la duplication, en exportant toute la manipulation dans sa propre méthode.
    Catch l'ancêtre commun le plus dérivé est d'éviter le travail extra et augmenter la clarté.
    DRY est un principe de conception important.

Dans les deux cas, à moins que vous n'espériez que cette exception et que vous l'ayez traitée complètement, revenez en arrière.

3
Deduplicator

Vous avez posté un lien vers Jongo, qui illustre une utilisation possible de cette technique: réutiliser le code de traitement des erreurs.

Supposons que vous disposiez d'un grand bloc de code de traitement des erreurs qui se répète naturellement à divers endroits dans votre code. Par exemple, Jongo génère des réponses standard pour certaines classes d'erreur standard. Il peut être judicieux d'extraire ce code de gestion des erreurs dans une méthode afin de pouvoir le réutiliser à partir de tous les emplacements nécessaires.

Cependant, cela ne veut pas dire qu'il n'y a rien de mal avec le code de Jongo.

Attraper Throwable (plutôt que d’utiliser plusieurs correspondances) est toujours suspect, car vous attraperez probablement Errors que vous n'êtes pas vraiment en mesure de gérer (êtes-vous bien sûr avez-vous voulu attraper ThreadDeath?) Dans cette situation, si vous devez absolument attraper Throwable, il vaudrait mieux "attraper et relâcher" (c'est-à-dire, tout ce que vous ne vouliez pas attraper). Jongo ne fait pas ça.

3
James_pic

Juste pour fournir un équilibre - il y a un endroit où je vais toujours catch (Throwable):

public static void main(String args[]) {
    try {
        new Test().test();
    } catch (Throwable t) {
        t.printStackTrace(System.err);
    }
}

Au moins, quelque chose montre quelque part que quelque chose s'est mal passé.

2
OldCurmudgeon

Tout d’abord, attraper Throwable rend votre application plutôt transparente. Vous devez être aussi explicite que possible sur la capture des exceptions pour permettre une bonne traçabilité dans des cas exceptionnels.

Jetons un coup d'oeil à la méthode handleException (...) et voyons quelques-uns des problèmes qui se produisent avec cette approche:

  • vous attrapez Throwable mais vous ne gérez que des Exceptions, que se passe-t-il si, par exemple, OutOfMemoryError de type Erreur est renvoyée? - Je vois de mauvaises choses arriver ...
  • S'agissant d'une bonne programmation orientée objet, utiliser instanceof casse le principe Open-Closed et libère les modifications de code (ajout de nouvelles exceptions, par exemple).

De mon point de vue, les blocs d'arrêt sont exactement conçus pour les fonctionnalités que nous essayons de couvrir dans handleExceptions (...), alors utilisez-les.

2
crazzle

Java 7 résout un peu l'ennui qui consiste à intercepter plusieurs exceptions similaires avec une gestion similaire. Vous ne devriez certainement pas faire ce que la personne a fait ici. Attrapez simplement les exceptions appropriées selon vos besoins, cela peut paraître moche mais alors c'est à quoi throws est destiné, passez-le à la méthode qui devrait l'attraper et vous ne devriez pas gaspiller trop d'espace de code.

Consultez ce lien pour plus d'informations.

2
insidesin

Vous pouvez toujours intercepter différents types d'exceptions et effectuer certaines opérations en fonction du type d'exception que vous avez obtenu.

Voici un exemple

          try{

              //do something that could throw an exception

             }catch (ConnectException e) {
                //do something related to connection


            } catch (InvalidAttributeValueException e) {
                // do anything related to invalid attribute exception

            } catch (NullPointerException e) {

                // do something if a null if obtained
            }

            catch (Exception e) {
            // any other exception that is not handled can be catch here, handle it here

            }
            finally{
          //perform the final operatin like closing the connections etc.
             }
0
Navankur Chauhan