b'\n_START_ARTICLE_\nGestione delle eccezioni in Java\n_START_SECTION_\nMotivazioni\n_START_PARAGRAPH_\nQualsiasi programma concreto di un certo livello di complessit\xc3\xa0 pu\xc3\xb2 incorrere, durante la propria esecuzione, in situazioni anomale che richiedono di venire trattate eseguendo azioni che differiscono da quello che sarebbe stato, altrimenti, il "flusso normale" del programma. Ovviamente, il confine fra "anomalo" o "normale" non \xc3\xa8 netto. Come esempi di "situazioni anomale", si pensi per esempio all\'impossibilit\xc3\xa0 di dialogare con un server attraverso la rete, o il fatto che il programma cerchi di aprire un file che non risulta presente su disco, e cos\xc3\xac via._NEWLINE_La gestione delle situazioni anomale presenta diversi aspetti critici rispetto a considerazioni di qualit\xc3\xa0 del software. Da una parte, sarebbe auspicabile che chi sviluppa un programma ponga una notevole cura nel prevedere tutte le possibili situazioni anomale che potrebbero insorgere durante l\'esecuzione e nel predisporre le contromisure che il programma deve adottare in tali casi per ridurre al minimo le conseguenze di tali anomalie. La gestione "puntigliosa" di tutte le possibili situazioni anomale in tutti i possibili luoghi del codice in cui possono manifestarsi \xc3\xa8 infatti importante ai fini della robustezza e affidabilit\xc3\xa0 del software. D\'altra parte, essendo le situazioni "anomale" potenzialmente molto numerose e diversificate, una gestione davvero completa potrebbe avere l\'effetto indesiderabile di oscurare la struttura del codice sorgente, poich\xc3\xa9 le (relativamente poche) istruzioni che il programma dovrebbe eseguire nel caso normale (o nei casi normali) si potrebbero trovare immerse (e "disperse") in mezzo a una quantit\xc3\xa0 preponderante di istruzioni dedicate alla gestione di anomalie (magari molto improbabili), ovviamente a scapito della leggibilit\xc3\xa0 del programma stesso.\n_START_SECTION_\nSegnalazione del fallimento di un metodo\n_START_PARAGRAPH_\nOgni metodo di un programma Java dovrebbe avere un compito ben preciso da portare a termine (descritto dal suo commento Javadoc). In presenza di anomalie o situazioni impreviste, \xc3\xa8 possibile che un metodo fallisca, ovvero non sia in grado di portare a termine tale compito. Questa evenienza deve essere evidentemente segnalata al metodo chiamante il quale poi potr\xc3\xa0, a seconda dei casi, prendere qualche contromisura che gli consenta di concludere il proprio compito nonostante il fallimento del metodo chiamato, oppure, se questo \xc3\xa8 impossibile, dichiarare a sua volta il proprio fallimento nei confronti del proprio chiamante (e cos\xc3\xac via)._NEWLINE_Per segnalare il proprio fallimento, un metodo Java pu\xc3\xb2 sollevare (o "lanciare", per conservare il significato del corrispondente termine inglese to throw) una eccezione. Si pu\xc3\xb2 considerare una eccezione sollevata da un metodo come analogo al concetto di valore restituito dal metodo. Tuttavia, Java distingue i due concetti, cos\xc3\xac che un metodo potrebbe per esempio tornare un valore intero oppure sollevare un\'eccezione, che \xc3\xa8 un valore di altro tipo (in seguito vedremo quali sono i tipi ammissibili per i valori-eccezione). Il seguente estratto di codice mostra una situazione di questo genere:_NEWLINE_/** * Calcola la differenza in giorni fra due date, specificate rispettivamente dal giorno * "gg1", mese "mm1" e anno "aa1" e giorno "gg2", mese "mm2" e anno "aa2"*/public int differenzaDate(int gg1, int mm1, int aa1, int gg2, int mm2, int aa2)_NEWLINE_ throws DataNonValida_NEWLINE_ {_NEWLINE_    if(!dataValida(gg1, mm1, aa1) || !dataValida(gg2, mm2, aa2))_NEWLINE_        throw new DataNonValida();_NEWLINE_    else {_NEWLINE_        int risultato;_NEWLINE_        // ... calcola la differenza fra le date_NEWLINE_        return risultato;_NEWLINE_    }}_NEWLINE_Il metodo riportato ritorna, in assenza di errori, un intero che rappresenta la distanza in giorni fra due date. Nel caso in cui una delle triple (giorno, mese, anno) non sia una data valida (per esempio, la tripla 31 2 2000), anzich\xc3\xa9 ritornare un valore intero il metodo solleva una eccezione. La clausola throws nell\'intestazione del metodo specifica che questo speciale valore di "eccezione" sar\xc3\xa0 di tipo (classe) DataNonValida; il punto del codice in cui l\'eccezione viene sollevata \xc3\xa8 l\'istruzione throw new DataNonValida(). Ovviamente deve essere stata definita una classe DataNonValida e, come vedremo nel seguito, questa classe deve anche avere alcune caratteristiche specifiche che consentono l\'utilizzo delle sue istanze come valori di eccezione._NEWLINE_La semantica dell\'operatore throw ha alcuni aspetti in comune con quella dell\'operatore return; in particolare, l\'esecuzione dell\'istruzione throw comporta la terminazione immediata del metodo e il passaggio del controllo (seppure secondo un particolare insieme di regole che si esamineranno nel seguito) al chiamante del metodo stesso.\n_START_SECTION_\nLa regola "handle or declare"\n_START_PARAGRAPH_\nPotrebbe darsi il caso in cui, a differenza di quanto visto nell\'esempio precedente, il metodo chiamante non sia in grado di prendere contromisure rispetto al problema occorso. Supponiamo per esempio che il metodo che riceve dall\'input i valori dei giorni, mesi e anni non sia faiQualcosa ma il suo chiamante (e che quindi, faiQualcosa riceva questi dati come argomenti). In tal caso \xc3\xa8 sensato supporre che sia opportuno delegare al chiamante di faiQualcosa anche la soluzione del problema (cio\xc3\xa8 chiedere nuovi dati all\'utente). Il blocco di codice seguente mostra quale dovrebbe essere la forma del metodo faiQualcosa in questo caso:_NEWLINE_public void faiQualcosa(int g1, int m1, int a1, int g2, int m2, int a2) _NEWLINE_ throws DataNonValida_NEWLINE_ {_NEWLINE_    int dd = differenzaDate(g1, m1, a1, g2, m2, a2);_NEWLINE_    System.out.println("La differenza \xc3\xa8 " + dd);}_NEWLINE_Poich\xc3\xa9 faiQualcosa non pu\xc3\xb2 risolvere il problema eventualmente segnalato da differenzaDate, in questa versione esso non contiene una clausola try-catch. In questo caso, se differenzaDate solleva effettivamente l\'eccezione, il modello di exception handling di Java prevede che anche faiQualcosa venga terminato. L\'eccezione sollevata da differenzaDate sar\xc3\xa0 in tal caso automaticamente "propagata" al chiamante di faiQualcosa, esattamente come se quest\'ultimo avesse eseguito l\'istruzione throw. Per questo motivo, diventa obbligatorio inserire la clausola throws DataNonValida anche nell\'intestazione di faiQualcosa, segnalando cos\xc3\xac il fatto che anche questo metodo (indirettamente) pu\xc3\xb2 riportare una eccezione di tipo DataNonValida al proprio chiamante._NEWLINE_Questa regola di Java (innovativa rispetto all\'exception handling del C++) viene detta regola handle or declare (gestisci o dichiara): a fronte di una possibile eccezione, un metodo deve gestirla oppure dichiarare a sua volta di sollevarla. Questo modello implica che una eccezione non possa mai passare inosservata; se non la si gestisce, non si fa altro che rimandare al chiamante l\'obbligo di gestirla.\n_START_SECTION_\nEccezioni come oggetti\n_START_PARAGRAPH_\nNegli esempi precedenti, i valori usati come "eccezioni" erano istanze di una classe Java (DataNonValida). A differenza di quanto avviene in C++, Java non ammette l\'uso di tipi primitivi come valori-eccezione; le eccezioni, cio\xc3\xa8, devono essere oggetti. Pi\xc3\xb9 in particolare, le classi definite per rappresentare le eccezioni devono estendere la classe Throwable (letteralmente: "che pu\xc3\xb2 essere lanciato"). A parte questo vincolo, la definizione di una classe di eccezione \xc3\xa8 libera. Molto spesso, in particolare, si usano attributi e metodi per corredare l\'oggetto-eccezione di informazioni specifiche sul tipo di errore verificatosi._NEWLINE_Si consideri la seguente definizione:_NEWLINE_public class DataNonValida extends Throwable {_NEWLINE_    private int g, m, a;_NEWLINE__NEWLINE_    public DataNonValida(int g, int m, int a) {_NEWLINE_        this.g = g;_NEWLINE_        this.m = m;_NEWLINE_        this.a = a;_NEWLINE_    }_NEWLINE__NEWLINE_    public int getGiorno() { return g; }_NEWLINE_    public int getMese() { return m; }_NEWLINE_    public int getAnno() { return a; }}_NEWLINE_Questa classe rappresenta una anomalia di data non valida; le sue istanze sono anche in grado di memorizzare nei propri attributi i valori di giorno, mese e anno di cui si \xc3\xa8 rilevata la non validit\xc3\xa0. Si consideri ora questa riscrittura del metodo differenzaDate:_NEWLINE_public int differenzaDate(int gg1, int mm1, int aa1, int gg2, int mm2, int aa2)_NEWLINE_ throws DataNonValida_NEWLINE_ {_NEWLINE_    if(!dataValida(gg1, mm1, aa1))_NEWLINE_        throw new DataNonValida(gg1, mm1, aa1);_NEWLINE_    else if(!dataValida(gg2, mm2, aa2))_NEWLINE_        throw new DataNonValida(gg2, mm2, aa2);_NEWLINE_    else {_NEWLINE_        int risultato;_NEWLINE_        // ... calcola la differenza fra le date_NEWLINE_        return risultato;_NEWLINE_    }}_NEWLINE_In questa variante, il metodo segnala l\'anomalia occorsa generando un oggetto-eccezione che, a differenza dei casi precedenti, viene corredato dell\'informazione aggiuntiva circa i valori di giorno, mese e anno che sono risultati scorretti. Questo potrebbe servire al chiamante, per esempio, per chiedere all\'utente il reinserimento solo di una delle due date inserite (quella che si \xc3\xa8 rivelata scorretta)._NEWLINE_Il seguente frammento di codice mostra come le informazioni inserite nell\'oggetto-eccezione diventino disponibili a chi "cattura" (catch) l\'eccezione stessa:_NEWLINE_try {_NEWLINE_    int dd = differenzaDate(g1, m1, a1, g2, m2, a2);_NEWLINE_    ...} catch (DataNonValida dnv) {_NEWLINE_    System.out.println("La data " + dnv.getGiorno() + "/" + dnv.getMese() + "/" + dnv.getAnno() + " non \xc3\xa8 corretta");}_NEWLINE_L\'identificatore dnv che compare nella clausola catch gioca un ruolo analogo a quello di un parametro di un metodo. Esso cio\xc3\xa8 identifica un reference a cui viene assegnato l\'oggetto-eccezione "lanciato" dall\'istruzione throw. Tale oggetto pu\xc3\xb2 quindi essere manipolato come un oggetto qualsiasi, per esempio per estrarne informazione.\n_START_SECTION_\nEccezioni e catch multiple\n_START_PARAGRAPH_\nUn metodo pu\xc3\xb2 sollevare pi\xc3\xb9 tipi di eccezione. Per esempio, un metodo che deve accedere a file potrebbe prevedere diverse segnalazioni di anomalie che rappresentano il fatto che il file non esista oppure che i suoi contenuti risultino danneggiati o scorretti:_NEWLINE_public int leggiFile() throws FileInesistente, FileDanneggiato {_NEWLINE_    ...}_NEWLINE_Analogamente, un blocco try-catch pu\xc3\xb2 comprendere pi\xc3\xb9 blocchi catch dedicati a gestire diversi tipi di eccezioni:_NEWLINE_public faiQualcosa2() {_NEWLINE_    try {_NEWLINE_        leggiFile();_NEWLINE_    } catch (FileInesistente fi) {_NEWLINE_        System.out.println("Ooops! Il file " + fi.getNomeFile() + " non esiste!");_NEWLINE_    } catch (FileDanneggiato fd) {_NEWLINE_        System.out.println("Ooops! Il file " + fd.getNomeFile() + " contiene dati scorretti!");_NEWLINE_    }}\n_START_SECTION_\nClassificazione delle eccezioni\n_START_PARAGRAPH_\nSebbene al programmatore sia consentito scrivere proprie classi di eccezione, Java dispone gi\xc3\xa0 di una propria gerarchia di classi-eccezione, di cui \xc3\xa8 rilevante conoscere la struttura generale._NEWLINE_Come si \xc3\xa8 detto, tutte le eccezioni sono Throwable. Le due sottoclassi dirette di Throwable sono Error e Exception. La classe Error dovrebbe essere riservata per situazioni anomale legate a malfunzionamenti della macchina virtuale Java; in genere, queste non dovrebbero essere gestite, perch\xc3\xa9 corrispondono a situazioni che per definizione si considerano irrecuperabili. Se per esempio un programma entra in una condizione di stack overflow, l\'esecuzione di qualsiasi istruzione diventa inaffidabile; la terminazione del programma risulta quindi l\'opzione pi\xc3\xb9 ragionevole._NEWLINE_Le Exception sono invece le eccezioni che possono essere gestite. La sottoclasse RuntimeException rappresenta quelle eccezioni che vengono sollevate dalla macchina virtuale Java (e quindi non da una istruzione throw del programma). Per esempio, il tentativo da parte del programma di usare un reference di valore null comporta la segnalazione di una RuntimeException da parte della JVM. Queste eccezioni possono essere catturate e gestite. Tuttavia, essendo in un certo senso eccezioni spontanee (non generate esplicitamente dal programma), esse non vengono dichiarate nelle clausole throws (in un certo senso, si assume che qualunque metodo possa incorrere in anomalie di questo genere, per cui dichiarare questa possibilit\xc3\xa0 in modo sistematico, con il conseguente obbligo di gestione dato dalla regola handle or declare, diverrebbe troppo oneroso).'