Gestione delle eccezioni in Java: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
→Segnalazione del fallimento di un metodo: corr disamb |
m tag source deprecati, replaced: <source lang= → <syntaxhighlight lang= (9), </source> → </syntaxhighlight> (9) |
||
Riga 15:
Per segnalare il proprio fallimento, un metodo Java può ''sollevare'' (o "lanciare", per conservare il significato del corrispondente termine inglese ''to throw'') una eccezione. Si può considerare una eccezione sollevata da un metodo come analogo al concetto di ''valore restituito'' dal metodo. Tuttavia, Java distingue i due concetti, così che un metodo potrebbe per esempio tornare un valore intero ''oppure'' sollevare un'eccezione, che è 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:
<
/**
* Calcola la differenza in giorni fra due date, specificate rispettivamente dal giorno
Riga 31:
}
}
</syntaxhighlight>
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é ritornare un valore intero il metodo solleva una eccezione. La clausola <code>throws</code> nell'intestazione del metodo specifica che questo speciale valore di "eccezione" sarà di tipo ([[Classe (informatica)|classe]]) <code>DataNonValida</code>; il punto del codice in cui l'eccezione viene sollevata è l'[[istruzione]] <code>throw new DataNonValida()</code>. Ovviamente deve essere stata definita una classe <code>DataNonValida</code> e, come vedremo nel seguito, questa classe deve anche avere alcune caratteristiche specifiche che consentono l'utilizzo delle sue istanze come valori di eccezione.
Riga 42:
Il seguente frammento di codice mostra l'uso del blocco try-catch nel chiamante:
<
public void faiQualcosa(Scanner input) {
boolean successo = false;
Riga 59:
}
}
</syntaxhighlight>
La clausola <code>try</code> controlla un [[blocco (programmazione)|blocco di codice]] all'interno del quale compare il metodo "a rischio" ''differenzaDate''. Quando il metodo viene eseguito, si danno due casi:
Riga 70:
Potrebbe 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 è sensato supporre che sia opportuno delegare al chiamante di ''faiQualcosa'' anche la soluzione del problema (cioè chiedere nuovi dati all'utente). Il blocco di codice seguente mostra quale dovrebbe essere la forma del metodo ''faiQualcosa'' in questo caso:
<
public void faiQualcosa(int g1, int m1, int a1, int g2, int m2, int a2)
throws DataNonValida
Riga 77:
System.out.println("La differenza è " + dd);
}
</syntaxhighlight>
Poiché ''faiQualcosa'' non può 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à 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 <code>throws DataNonValida</code> anche nell'intestazione di ''faiQualcosa'', segnalando così il fatto che anche questo metodo (indirettamente) può riportare una eccezione di tipo ''DataNonValida'' al proprio chiamante.
Riga 94:
Si consideri la seguente definizione:
<
public class DataNonValida extends Throwable {
private int g, m, a;
Riga 108:
public int getAnno() { return a; }
}
</syntaxhighlight>
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 è rilevata la non validità. Si consideri ora questa riscrittura del metodo ''differenzaDate'':
<
public int differenzaDate(int gg1, int mm1, int aa1, int gg2, int mm2, int aa2)
throws DataNonValida
Riga 126:
}
}
</syntaxhighlight>
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 è rivelata scorretta).
Riga 132:
Il seguente frammento di codice mostra come le informazioni inserite nell'oggetto-eccezione diventino disponibili a chi "cattura" (''catch'') l'eccezione stessa:
<
try {
int dd = differenzaDate(g1, m1, a1, g2, m2, a2);
Riga 139:
System.out.println("La data " + dnv.getGiorno() + "/" + dnv.getMese() + "/" + dnv.getAnno() + " non è corretta");
}
</syntaxhighlight>
L'identificatore <code>dnv</code> che compare nella clausola <code>catch</code> gioca un ruolo analogo a quello di un [[parametro (informatica)|parametro]] di un metodo. Esso cioè identifica un ''[[riferimento in Java|reference]]'' a cui viene assegnato l'oggetto-eccezione "lanciato" dall'istruzione <code>throw</code>. Tale oggetto può quindi essere manipolato come un oggetto qualsiasi, per esempio per estrarne informazione.
Riga 146:
Un metodo può sollevare più 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:
<
public int leggiFile() throws FileInesistente, FileDanneggiato {
...
}
</syntaxhighlight>
Analogamente, un blocco try-catch può comprendere più blocchi catch dedicati a gestire diversi tipi di eccezioni:
<
public faiQualcosa2() {
try {
Riga 164:
}
}
</syntaxhighlight>
== Eccezioni e polimorfismo ==
Riga 185:
Questa regola contribuisce a garantire che ''tutte'' le eccezioni vengano sempre gestite. Si consideri il seguente blocco di codice:
<
public class X {
public void faiQualcosa() throws C { ... }
Riga 199:
}
}
</syntaxhighlight>
In virtù del polimorfismo, sappiamo che il metodo ''m'' potrebbe essere invocato con un argomento che non è di classe X, ma di una sua sottoclasse qualsiasi. In virtù dell'''overriding'' e del [[binding dinamico]], quindi, non abbiamo garanzie che il metodo ''faiQualcosa'' chiamato in ''m'' sia ''esattamente'' quello definito nella classe X; esso potrebbe infatti essere stato ridefinito in qualche sottoclasse di X. A fronte di questa incertezza, però, le regole descritte sopra ci garantiscono che qualunque ridefinizione di ''faiQualcosa'' in qualunque classe non potrà sollevare eccezioni non gestite dalla ''catch'' del metodo ''m''; questo infatti si verificherebbe solo se tale ridefinizione sollevasse eccezioni che non sono né di classe ''C'' né di sue sottoclassi, ciò che appunto le regole riportate sopra escludono.
|