Gestione delle eccezioni in Java: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
AttoBot (discussione | contributi)
m Bot: formattazione dei wikilink; modifiche estetiche
m Bot: inserimento portale (via JWB)
 
(13 versioni intermedie di 11 utenti non mostrate)
Riga 1:
{{NN|informatica|febbraio 2013}}
{{F|linguaggi di programmazione|febbraio 2013|Voce ampia che manca completamente di Note, Bibliografia, Collegamenti esterni}}
{{torna a|Java (linguaggio di programmazione)}}
Nel [[linguaggio di programmazione]] ''[[Programmazione orientata agli oggetti|object-oriented]]'' [[Java (linguaggio di programmazione)|Java]], il sistema di '''gestione delle eccezioni''' (o '''exception handling''') è costituito da un insieme di costrutti e regole [[sintassi (informatica)|sintattiche]] e [[semantica (informatica)|semantiche]] introdotte allo scopo di rendere più semplice, chiara e sicura la gestione di eventuali ''situazioni anomale'', dette [[eccezione (informatica)|eccezioni]], che si possono verificare durante l'[[esecuzione (informatica)|esecuzione]] di un [[Programma (informatica)|programma]].
 
L'''exception handling'' di Java deriva direttamente (anche da un punto di vista sintattico) da quello del [[C++|linguaggio C++]]. Tuttavia, il meccanismo di Java deve considerarsi forsedecisamente più oneroso, ma certamente più sicuro, grazie alla cosiddetta regola dello ''handle or declare'' (gestisci o dichiara), che in sostanza ''obbliga'' il [[programmatore]] a prevedere esplicite contromisure ''per ogni'' situazione anomala (prevedibile).
 
== Motivazioni ==
Riga 11:
 
== Segnalazione del fallimento di un metodo ==
Ogni [[Metodo (programmazione)|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, è possibile che un metodo ''fallisca'', ovvero non sia in grado di portare a termine tale compito. Questa evenienza deve essere evidentemente segnalata al metodo [[chiamata di metodo|chiamante]] il quale poi potrà, a seconda dei casi, prendere qualche contromisura che gli consenta di concludere il ''proprio'' compito nonostante il fallimento del metodo chiamato, oppure, se questo è impossibile, dichiarare a sua volta il proprio fallimento nei confronti del proprio chiamante (e così via).
 
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:
 
<sourcesyntaxhighlight lang="java">
/**
* Calcola la differenza in giorni fra due date, specificate rispettivamente dal giorno
Riga 31:
}
}
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="java">
public void faiQualcosa(Scanner input) {
boolean successo = false;
Riga 59:
}
}
</syntaxhighlight>
</source>
 
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 66:
 
In sostanza, il blocco try-catch consente di ''separare'' accuratamente il funzionamento del metodo nel caso "normale" (blocco try) e la gestione di situazioni anomale (blocco catch).
 
=== Il blocco finally ===
Come altri linguaggi, Java supporta un terzo blocco chiamato <syntaxhighlight lang="java" inline>finally</syntaxhighlight>. Questo blocco verrà eseguito sempre, a prescindere dal fatto che l'esecuzione del blocco try abbia generato o meno un'eccezione, e a prescindere da ciò che accade nel blocco catch.<ref>{{Cita web|lingua=en|url=https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html|titolo=The finally Block|opera=Java Documentation|editore=[[Oracle Corporation]]|urlarchivio=https://web.archive.org/web/20240127154417/https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html|urlmorto=no|accesso=20 marzo 2024|dataarchivio=27 gennaio 2024}}</ref>
La sintassi è la seguente:
 
<sourcesyntaxhighlight lang="java">
try {
// codice che può generare un'eccezione
} catch (ClasseEccezione e) {
// gestisci l'eccezione
} finally {
// codice da eseguire in ogni caso
</syntaxhighlight>
 
=== Eccezioni e catchCatch multiple ===
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:
 
<sourcesyntaxhighlight lang="java">
public int leggiFile() throws FileInesistente, FileDanneggiato {
...
</syntaxhighlight>
 
Analogamente, un blocco try-catch può comprendere più blocchi catch dedicati a gestire diversi tipi di eccezioni:
 
<syntaxhighlight lang="java">
public faiQualcosa2() {
}try {
leggiFile();
} catch (FileInesistente fi) {
System.out.println("Ooops! Il file " + fi.getNomeFile() + " non esiste!");
} catch (FileDanneggiato fd) {
System.out.println("Ooops! Il file " + fd.getNomeFile() + " contiene dati scorretti!");
}
}
</syntaxhighlight>
 
== La regola "handle or declare" ==
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:
 
<sourcesyntaxhighlight lang="java">
public void faiQualcosa(int g1, int m1, int a1, int g2, int m2, int a2)
throws DataNonValida
Riga 77 ⟶ 114:
System.out.println("La differenza è " + dd);
}
</syntaxhighlight>
</source>
 
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 ⟶ 131:
Si consideri la seguente definizione:
 
<sourcesyntaxhighlight lang="java">
public class DataNonValida extends Throwable {
private int g, m, a;
Riga 108 ⟶ 145:
public int getAnno() { return a; }
}
</syntaxhighlight>
</source>
 
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'':
 
<sourcesyntaxhighlight lang="java">
public int differenzaDate(int gg1, int mm1, int aa1, int gg2, int mm2, int aa2)
throws DataNonValida
Riga 126 ⟶ 163:
}
}
</syntaxhighlight>
</source>
 
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 ⟶ 169:
Il seguente frammento di codice mostra come le informazioni inserite nell'oggetto-eccezione diventino disponibili a chi "cattura" (''catch'') l'eccezione stessa:
 
<sourcesyntaxhighlight lang="java">
try {
int dd = differenzaDate(g1, m1, a1, g2, m2, a2);
Riga 139 ⟶ 176:
System.out.println("La data " + dnv.getGiorno() + "/" + dnv.getMese() + "/" + dnv.getAnno() + " non è corretta");
}
</syntaxhighlight>
</source>
 
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.
 
== Eccezioni e catch multiple ==
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:
 
<source lang="java">
public int leggiFile() throws FileInesistente, FileDanneggiato {
...
</source>
 
Analogamente, un blocco try-catch può comprendere più blocchi catch dedicati a gestire diversi tipi di eccezioni:
 
<source lang="java">
public faiQualcosa2() {
try {
leggiFile();
} catch (FileInesistente fi) {
System.out.println("Ooops! Il file " + fi.getNomeFile() + " non esiste!");
} catch (FileDanneggiato fd) {
System.out.println("Ooops! Il file " + fd.getNomeFile() + " contiene dati scorretti!");
}
</source>
 
== Eccezioni e polimorfismo ==
Se le eccezioni sono descritte da classi che implementanoestendono ''Throwable'', è possibile che diverse classi-eccezione siano legate da relazioni di [[ereditarietà (informatica)|ereditarietà]]. In accordo con i principi generali del paradigma orientato agli oggetti, si avranno relazioni del genere fra classi che rappresentano rispettivamente tipi di eccezioni generali ([[Superclasse (informatica)|superclasse]]) e casi particolari ([[Sottoclasse (informatica)|sottoclassi]]). Per esempio, la classe ''FileInesistente'' e la classe ''FileDanneggiato'' potrebbero essere sottoclassi di una classe ''ProblemaAccessoAlFile'' (questa classe potrebbe per esempio definire il metodo ''getNomeFile'' usato negli esempi precedenti).
 
Il [[polimorfismo (informatica)|polimorfismo]] (legato alle relazioni di ereditarietà) gioca un ruolo importante nella gestione delle eccezioni in Java. Per esempio, una clausola ''catch'' il cui "parametro" sia dichiarato di tipo ''ProblemaAccessoAlFile'' potrebbe catturare tanto eccezioni di tipo ''FileInesistente'' quanto eccezioni di tipo ''FileDanneggiato'' (in analogia con l'applicazione del polimorfismo al passaggio parametri verso metodi e [[Costruttore (programmazioneinformatica)|costruttori]]). Su considerazioni generali sul polimorfismo e il suo corretto uso, si veda la [[polimorfismo (informatica)|voce corrispondente]]. Nel caso estremo, un blocco ''catch'' con parametro di tipo ''Throwable'', per definizione, può catturare eccezioni di ''qualsiasi tipo''.
 
Per motivi analoghi, se un metodo dichiara di sollevare eccezioni di una certa classe ''C'', questo è assolutamente compatibile con l'eventualità che, sempre o in alcuni casi, tale metodo sollevi in effetti eccezioni di ''sottoclassi'' di ''C''.
Riga 185 ⟶ 199:
Questa regola contribuisce a garantire che ''tutte'' le eccezioni vengano sempre gestite. Si consideri il seguente blocco di codice:
 
<sourcesyntaxhighlight lang="java">
public class X {
public void faiQualcosa() throws C { ... }
Riga 199 ⟶ 213:
}
}
</syntaxhighlight>
</source>
 
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.
Riga 209 ⟶ 223:
 
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à in modo sistematico, con il conseguente obbligo di gestione dato dalla regola [[handle or declare]], diverrebbe troppo oneroso).
 
== Note ==
<references/>
 
== Voci correlate ==
Riga 214 ⟶ 231:
 
== Altri progetti ==
{{Interprogetto|b=Java/Gestione delle eccezioni}}
 
== Collegamenti esterni ==
* {{Collegamenti esterni}}
* {{Cita web|lingua=en|url=https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html|titolo=Lesson: Exception|opera=Java Documentation|editore=[[Oracle Corporation]]|urlarchivio=https://web.archive.org/web/20240127024950/https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html|urlmorto=no|accesso=20 marzo 2024|dataarchivio=27 gennaio 2024}}
* {{Cita web|lingua=en|url=https://www.w3schools.com/java/java_try_catch.asp|titolo=Java Exceptions (Try...Catch)|urlarchivio=https://web.archive.org/web/20240319040816/https://www.w3schools.com/java/java_try_catch.asp|urlmorto=no|accesso=20 marzo 2024|dataarchivio=19 marzo 2024}}
 
{{Portale|informatica}}
 
[[Categoria:Linguaggio Java]]