W3docs

Eccezioni Checked vs. Unchecked in Java

Differenza tra eccezioni checked e unchecked (runtime) in Java e quando usare ciascuna.

Java divide le sue eccezioni in due gruppi, e il compilatore le tratta in modo diverso. Le eccezioni checked devono essere catturate o dichiarate; il compilatore rifiuta di compilare il codice che le ignora. Le eccezioni unchecked non hanno questo requisito — il compilatore le lascia propagare silenziosamente e solo il runtime le intercetta. Questa divisione è una delle decisioni più caratteristiche e più dibattute di Java, e comprenderla è fondamentale per scrivere codice robusto.

Quale è quale

Tutto ciò che estende Throwable rientra in uno di tre gruppi:

  • Error — catastrofe a livello JVM. Unchecked. Non catturare.
  • RuntimeException (una sottoclasse di Exception) e tutti i suoi sottotipi. Unchecked.
  • Altre sottoclassi di Exception. Checked.

La regola è: "RuntimeException e Error insieme ai loro sottotipi sono unchecked; tutto il resto sotto Throwable è checked." Si tratta di una distinzione nell'albero delle classi, non di un flag di configurazione — non è possibile rendere una RuntimeException checked o una IOException unchecked.

Cosa significa "checked" nel codice

Se il corpo di un metodo lancia un'eccezione checked, il metodo deve fare una delle due cose seguenti, altrimenti il programma non verrà compilato:

// Option 1: catch it
public void load() {
  try {
    String text = Files.readString(path);   // throws IOException
  } catch (IOException e) {
    // ...
  }
}

// Option 2: declare it
public void load() throws IOException {
  String text = Files.readString(path);
}

Il compilatore analizza ogni metodo e verifica: per ogni eccezione checked che potrebbe sfuggire, c'è un catch contenente o una clausola throws? In caso contrario, errore.

Le eccezioni unchecked non hanno tale requisito. Puoi lanciare una IllegalArgumentException da qualsiasi metodo senza modificare la firma, e ogni chiamante è libero di catturarla o meno.

L'idea di progettazione

La motivazione alla base delle eccezioni checked era: alcuni fallimenti sono esiti attesi dell'operazione — un file potrebbe non esistere, una rete potrebbe essere inattiva — e il linguaggio dovrebbe assicurarsi che i chiamanti ci pensino. Le eccezioni unchecked sono per gli errori di programmazione — dereference null, indice fuori dai limiti, argomenti illegali — dove la soluzione giusta è correggere il codice, non aggiungere un try/catch.

Questa distinzione sembra chiara. In pratica si piega:

  • Il codice a strati moltiplica le dichiarazioni. Una IOException di basso livello proveniente da un caricatore di configurazione non rimane lì — ogni metodo che la chiama deve gestirla o dichiararla. Quando l'eccezione raggiunge il controller, la firma elenca sei eccezioni checked non correlate.
  • Le lambda non le gestiscono bene. Stream.map(this::parseLine) non verrà compilato se parseLine lancia un'eccezione checked, perché apply di Function non ne dichiara una.
  • Il wrapping è ovunque. Un workaround comune è catturare immediatamente un'eccezione checked e rilanciarla come eccezione runtime, il che vanifica lo scopo originale.

La maggior parte del codice Java moderno usa meno eccezioni checked di quelle usate dalla libreria standard — a volte nessuna. I nuovi framework come Spring si basano quasi interamente sulle eccezioni runtime per questo motivo.

Quando usare ciascuna

Una regola pratica difendibile:

  • Usa checked quando il chiamante ha una strategia di recupero realistica specifica per il fallimento — ad es. riprovare su un errore di rete, usare un'alternativa su un errore di parsing. Obbligali a pensarci.
  • Usa unchecked quando il fallimento è un bug (IllegalArgumentException, NullPointerException, IllegalStateException) o quando nessun chiamante potrebbe ragionevolmente recuperare.

In caso di dubbio, preferisci unchecked. Il costo di sbagliare è minore e puoi sempre inasprire in seguito.

L'esperienza dal lato catch

Che un'eccezione sia checked o meno cambia l'aspetto del catch:

// checked: caller must catch or declare
try {
  parser.parse(path);
} catch (IOException e) {
  // handle
}

// unchecked: caller may catch but doesn't have to
try {
  return numbers.get(idx);
} catch (IndexOutOfBoundsException e) {
  return -1;
}

Dal punto di vista del catch non c'è differenza comportamentale — entrambi i blocchi catturano ciò che dichiarano. La divisione conta solo nel sito di chiamata che non ha catturato: un'eccezione checked forcerebbe una dichiarazione throws; una unchecked si propaga silenziosamente.

Un errore comune: catturare Exception

Poiché Exception è il genitore sia dei tipi checked che unchecked (eccetto Error), catch (Exception e) corrisponde a quasi tutto. Questo è spesso un code smell:

try { complexOperation(); }
catch (Exception e) { log("failed"); }    // hides bugs and real failures alike

Il problema non è che cattura — è che cattura troppo. Una NullPointerException qui indica un bug; una IOException indica un vero fallimento recuperabile; una RuntimeException da una libreria che non controlli potrebbe essere qualsiasi cosa. Trattarle in modo identico significa di solito non gestirne bene nessuna. Preferisci catturare i tipi specifici per cui hai un piano.

Un esempio pratico

Un piccolo programma con due metodi: uno dichiara una IOException checked e l'altro lancia una IllegalArgumentException unchecked. Il compilatore li tratta in modo diverso — solo quello checked forza la gestione nel sito di chiamata.

java— editable, runs on the server

Nota tre cose. Rimuovi throws java.io.IOException da maybeReadFile e il file non verrà compilato. Rimuovi il try/catch circostante dalla prima chiamata e il file non verrà compilato neanche. Ma non c'è try/catch attorno all'ultimo requirePositive(-7) — e questo viene compilato correttamente, anche se la chiamata manderà in crash il programma. Questo è il trattamento asimmetrico in un'unica schermata.

Cosa viene dopo

Abbiamo parlato di tipi come IOException, RuntimeException, Error — ma come si relazionano effettivamente? L'albero delle classi è piccolo e vale la pena memorizzarlo. Continua con Gerarchia delle eccezioni Java.

Pratica

Pratica
Quale affermazione sulle eccezioni checked è corretta?
Quale affermazione sulle eccezioni checked è corretta?
Was this page helpful?