W3docs

Java: blocchi catch multipli e multi-catch

Gestisci più tipi di eccezioni in Java con blocchi catch multipli o una clausola multi-catch (Tipo1 | Tipo2 e).

Un singolo try può essere seguito da qualsiasi numero di blocchi catch. Ognuno dichiara un tipo di eccezione diverso e viene eseguito solo se il lancio corrisponde. È così che Java permette a un blocco di codice di reagire diversamente a seconda di cosa è andato storto — un errore di rete non è un errore di analisi, e potresti volerli gestire in modi completamente diversi.

Catch sequenziali multipli

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");
}

Solo un catch viene eseguito per ogni lancio — il primo il cui tipo è una corrispondenza instance-of con l'eccezione lanciata. Dopo la sua esecuzione, il controllo salta all'istruzione dopo l'intero blocco try, non al catch successivo.

L'ordine è importante: dal più specifico al più generale

Un catch (T e) corrisponde a qualsiasi cosa che sia una T o qualsiasi sottoclasse di T. Se elenchi una superclasse prima della sua sottoclasse, il catch della sottoclasse diventa irraggiungibile e il compilatore lo rifiuta:

try { ... }
catch (Exception e)        { ... }   // matches everything below Exception
catch (IOException e)      { ... }   // ERROR: unreachable

L'ordine da seguire è tipo più ristretto in cima, più ampio in fondo:

try { ... }
catch (FileNotFoundException e) { ... }   // most specific
catch (IOException e)           { ... }   // wider
catch (Exception e)             { ... }   // catch-all (used sparingly)

Questo è anche un utile esercizio di progettazione: scrivere i catch ti costringe a pensare a quali fallimenti il blocco può effettivamente produrre.

Multi-catch (un blocco, più tipi)

Java 7 ha introdotto la forma multi-catch: un singolo blocco che gestisce più tipi di eccezioni non correlati, separati da |:

try {
  return parser.read(file);
} catch (IOException | ParseException e) {
  log.warn("could not load config: " + e);
  return Config.defaults();
}

Usa questa forma quando la gestione è identica per più fallimenti distinti. È più concisa di due blocchi catch quasi duplicati e rende ovvia a colpo d'occhio la relazione "questi condividono una risposta".

Regole da conoscere:

  • I tipi nell'unione non devono essere correlati per ereditarietà. IOException | FileNotFoundException non compilerà — uno è un sottotipo dell'altro, quindi il più ampio lo copre già.
  • All'interno del blocco, e è tipizzata come il supertipo comune dei tipi elencati. Puoi chiamare metodi dichiarati su quel supertipo, ma non quelli specifici del sottotipo. Per la maggior parte degli usi (logging, wrapping), getMessage() e toString() sono sufficienti.
  • Il parametro catch in un multi-catch è implicitamente final — non puoi riassegnarlo. (I catch a tipo singolo sono solo effettivamente final; la differenza non ha importanza in pratica.)

Quando separare un try

Un errore di leggibilità comune è avvolgere un intero metodo in un unico grande try, poi catturare tutto ciò che qualsiasi riga potrebbe lanciare. La logica di gestione in fondo diventa confusa riguardo a quale riga ha fallito.

Due strutture più pulite quando questo accade:

  • Due istruzioni try separate, ciascuna limitata a un insieme correlato di operazioni.
  • Un try all'interno di un metodo che chiama metodi più piccoli, ognuno responsabile di un tipo di fallimento.

Più piccolo è il try, più facili sono i catch da ragionare. "Quale riga potrebbe lanciare questo?" dovrebbe avere sempre una risposta breve.

Un esempio pratico

Un piccolo programma che fa tre cose — analizza un numero, lo cerca in un array, divide per esso — ognuna delle quali può fallire in modo diverso. Catturiamo ogni fallimento con un handler dedicato in modo che i messaggi rimangano specifici. L'ultimo catch usa la forma multi-catch per raggruppare due fallimenti che condividono una risposta.

java— editable, runs on the server

Ogni input prende un percorso diverso attraverso i catch, ma ogni iterazione stampa una riga pulita — il programma non si arrende mai di fronte all'utente.

Cosa c'è dopo

try/catch gestisce il percorso felice e il percorso di fallimento. La terza clausola, finally, gestisce le cose che devono accadere in ogni caso. Continua con il blocco finally di Java.

Esercitazione

Pratica
Questo codice compilerà?\n\n```java\ntry { ... }\ncatch (IOException | FileNotFoundException e) {\n log(e);\n}\n```
Questo codice compilerà?\n\n```java\ntry { ... }\ncatch (IOException | FileNotFoundException e) {\n log(e);\n}\n```
Was this page helpful?