Blocs catch multiples et multi-catch en Java
Gérez différents types d'exceptions en Java avec plusieurs blocs catch ou une clause multi-catch (Type1 | Type2 e).
Un seul try peut être suivi d'un nombre quelconque de blocs catch. Chacun déclare un type d'exception différent et ne s'exécute que si l'exception levée correspond. C'est ainsi que Java permet à un bloc de code de réagir différemment selon ce qui s'est mal passé — une défaillance réseau n'est pas une erreur d'analyse, et vous voudrez peut-être les gérer de manières complètement différentes.
Plusieurs blocs catch séquentiels
try {
String body = httpClient.get(url);
Config cfg = parser.parse(body);
apply(cfg);
} catch (IOException e) {
retryLater(url);
} catch (ParseException e) {
report("config file is malformed: " + e.getMessage());
} catch (SecurityException e) {
report("not allowed to apply config");
}Un seul bloc catch s'exécute par exception levée — le premier dont le type correspond à l'exception levée via instanceof. Une fois exécuté, le contrôle passe à l'instruction après l'ensemble de l'instruction try, et non au catch suivant.
L'ordre est important : du plus spécifique au plus général
Un catch (T e) correspond à tout ce qui est un T ou une sous-classe de T. Si vous listez une superclasse avant sa sous-classe, le catch de la sous-classe est inaccessible, et le compilateur refuse :
try { ... }
catch (Exception e) { ... } // matches everything below Exception
catch (IOException e) { ... } // ERROR: unreachableL'ordre à suivre est le type le plus spécifique en haut, le plus large en bas :
try { ... }
catch (FileNotFoundException e) { ... } // most specific
catch (IOException e) { ... } // wider
catch (Exception e) { ... } // catch-all (used sparingly)C'est aussi un exercice de conception utile : écrire les blocs catch vous oblige à réfléchir aux défaillances que le bloc peut réellement produire.
Multi-catch (un seul bloc, plusieurs types)
Java 7 a ajouté la forme multi-catch : un seul bloc gérant plusieurs types d'exceptions non liés, séparés par | :
try {
return parser.read(file);
} catch (IOException | ParseException e) {
log.warn("could not load config: " + e);
return Config.defaults();
}Utilisez cette forme lorsque le traitement est identique pour plusieurs défaillances distinctes. C'est plus court que deux blocs catch quasi-identiques et rend évidente la relation « ces cas partagent une réponse » au premier coup d'œil.
Règles à connaître :
- Les types dans l'union ne doivent pas être liés par héritage.
IOException | FileNotFoundExceptionne compilera pas — l'un est un sous-type de l'autre, donc le plus large le couvre déjà. - À l'intérieur du bloc,
eest typé comme le supertype commun des types listés. Vous pouvez appeler les méthodes déclarées sur ce supertype, mais pas celles spécifiques aux sous-types. Pour la plupart des usages (journalisation, encapsulation),getMessage()ettoString()suffisent. - Le paramètre catch dans un multi-catch est implicitement final — vous ne pouvez pas le réassigner. (Les catches à type unique sont seulement effectivement finaux ; la différence n'a pas d'importance en pratique.)
Quand diviser un try
Une erreur courante de lisibilité consiste à envelopper toute une méthode dans un seul try géant, puis à tout attraper depuis n'importe quelle ligne. La logique de traitement en bas finit par être confuse quant à la ligne qui a échoué.
Deux formes plus propres dans ce cas :
- Deux instructions try séparées, chacune limitée à un ensemble d'opérations connexes.
- Un try à l'intérieur d'une méthode qui appelle des méthodes plus petites, chacune responsable d'un type de défaillance.
Plus le try est petit, plus les blocs catch sont faciles à raisonner. « Quelle ligne pourrait lever cette exception ? » devrait toujours avoir une réponse courte.
Un exemple pratique
Un petit programme qui fait trois choses — analyser un nombre, le rechercher dans un tableau, diviser par lui — chacune pouvant échouer à sa façon. Nous capturons chaque défaillance avec un gestionnaire dédié pour que les messages restent précis. Le dernier catch utilise la forme multi-catch pour regrouper deux défaillances qui partagent une réponse.
Chaque entrée emprunte un chemin différent à travers les blocs catch, mais chaque itération affiche une ligne propre — le programme ne se retrouve jamais à lever les bras face à l'utilisateur.
Prochaine étape
try/catch gère le chemin heureux et le chemin d'échec. La troisième clause, finally, gère les choses qui doivent se produire dans tous les cas. Continuez vers le bloc finally en Java.