TL; DR
Premessa
- Le eccezioni di runtime devono essere generate quando l'errore è irrecuperabile: quando l'errore si trova nel codice e non dipende dallo stato esterno (quindi il ripristino correggerebbe il codice).
- Le eccezioni verificate devono essere generate quando il codice è corretto, ma lo stato esterno non è come previsto: nessuna connettività di rete, file non trovato o corrotto, ecc.
Conclusione
È possibile riproporre un'eccezione verificata come eccezione di runtime se il codice di propagazione o di interfaccia presuppone che l'implementazione sottostante dipenda dallo stato esterno, quando chiaramente non lo è.
Questa sezione tratta l'argomento di quando deve essere generata una delle eccezioni. Puoi passare alla barra orizzontale successiva se desideri solo leggere una spiegazione più dettagliata per la conclusione.
Quando è appropriato generare un'eccezione di runtime? Si genera un'eccezione di runtime quando è chiaro che il codice non è corretto e che il recupero è appropriato modificando il codice.
Ad esempio, è opportuno generare un'eccezione di runtime per quanto segue:
float nan = 1/0;
Ciò genererà una divisione per zero eccezioni di runtime. Questo è appropriato perché il codice è difettoso.
O per esempio, ecco una parte del HashMap
costruttore di:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
Al fine di correggere la capacità iniziale o il fattore di carico, è opportuno modificare il codice per assicurarsi che vengano passati i valori corretti. Non dipende dal fatto che un server remoto sia attivo, dallo stato corrente del disco, un file o un altro programma. Quel costruttore chiamato con argomenti non validi dipende dalla correttezza del codice chiamante, sia esso un calcolo errato che ha portato a parametri non validi o un flusso inappropriato che ha perso un errore.
Quando è appropriato lanciare un'eccezione controllata? Si genera un'eccezione controllata quando il problema è recuperabile senza modificare il codice. O per dirla in termini diversi, si genera un'eccezione controllata quando l'errore è correlato allo stato mentre il codice è corretto.
Ora la parola "recuperare" può essere complicata qui. Potrebbe significare che trovi un altro modo per raggiungere l'obiettivo: ad esempio, se il server non risponde, dovresti provare il server successivo. Se questo tipo di ripristino è possibile per il tuo caso, allora è fantastico, ma non è l'unica cosa che significa recupero: il ripristino potrebbe semplicemente mostrare all'utente una finestra di dialogo che spiega cosa è successo, o se si tratta di un'applicazione server, allora potrebbe essere inviando un'e-mail all'amministratore o semplicemente registrando l'errore in modo appropriato e conciso.
Facciamo l'esempio menzionato nella risposta di mrmuggles:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
Questo non è il modo corretto di gestire l'eccezione selezionata. La semplice incapacità di gestire l'eccezione nell'ambito di questo metodo non significa che l'app debba essere arrestata in modo anomalo. Invece, è opportuno propagarlo in un ambito superiore in questo modo:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Ciò consente la possibilità di recupero da parte del chiamante:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
Le eccezioni verificate sono uno strumento di analisi statica, che chiariscono per un programmatore cosa potrebbe andare storto in una determinata chiamata senza richiedere loro di apprendere l'implementazione o passare attraverso un processo di prova ed errore. Ciò semplifica l'assicurazione che nessuna parte del flusso di errori venga ignorata. Il rilancio di un'eccezione controllata come un'eccezione di runtime funziona con questa funzionalità di analisi statica che fa risparmiare lavoro.
Vale anche la pena ricordare che il livello chiamante ha un migliore contesto dello schema più grande delle cose, come è stato dimostrato sopra. Potrebbero esserci molte cause che dataAccessCode
potrebbero essere chiamate, il motivo specifico della chiamata è visibile solo al chiamante, quindi è in grado di prendere una decisione migliore al corretto recupero in caso di fallimento.
Ora che abbiamo chiarito questa distinzione, potremmo procedere a dedurre quando è giusto ricodificare un'eccezione controllata come eccezione di runtime.
Considerato quanto sopra, quando è appropriato ricodificare un'eccezione verificata come RuntimeException? Quando il codice che stai utilizzando assume la dipendenza dallo stato esterno, ma puoi affermare chiaramente che non dipende dallo stato esterno.
Considera quanto segue:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
In questo esempio, il codice si sta propagando IOException
perché l'API di Reader
è progettata per accedere allo stato esterno, tuttavia sappiamo che l' StringReader
implementazione non accede allo stato esterno. In questo ambito, dove possiamo certamente affermare che le parti coinvolte nella chiamata non accedono a IO o a qualsiasi altro stato esterno, possiamo tranquillamente riproporre l'eccezione come eccezione di runtime senza stupire i colleghi che non sono consapevoli della nostra implementazione (e sono probabilmente supponendo che il codice di accesso IO genererà un IOException
).
La ragione per mantenere strettamente statali esterno dipendenti eccezioni controllate è che sono non deterministici (a differenza di logica eccezioni dipendenti, che saranno prevedibilmente essere riprodotti ogni volta per una versione del codice). Ad esempio, se si tenta di dividere per 0, si produrrà sempre un'eccezione. Se non dividi per 0, non produrrai mai un'eccezione e non dovrai gestire quel caso di eccezione, perché non accadrà mai. Nel caso di accedere a un file, tuttavia, riuscire una volta non significa che ci riuscirai la prossima volta: l'utente potrebbe aver cambiato le autorizzazioni, un altro processo potrebbe averlo cancellato o modificato. Quindi devi sempre gestire quel caso eccezionale, o probabilmente hai un bug.