Gestione delle eccezioni in Java: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
m Bot: formattazione dei wikilink |
m Bot: formattazione dei wikilink; modifiche estetiche |
||
Riga 1:
{{F|linguaggi di programmazione|febbraio 2013|Voce ampia che manca completamente di Note, Bibliografia, Collegamenti esterni}}
{{torna a|Java (linguaggio)}}
Nel [[linguaggio di programmazione]] ''[[Programmazione orientata agli oggetti|
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 forse 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 ==
Qualsiasi programma concreto di un certo livello di complessità può 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 di esecuzione|flusso]] normale" del programma. Ovviamente, il confine fra "anomalo" o "normale" non è netto. Come esempi di "situazioni anomale", si pensi per esempio all'impossibilità 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ì via.
La gestione delle situazioni anomale presenta diversi aspetti critici rispetto a considerazioni di [[qualità 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 è infatti importante ai fini della [[robustezza (software)|robustezza]] e [[affidabilità (software)|affidabilità]] 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é le (relativamente poche) [[istruzione (informatica)|istruzioni]] che il programma dovrebbe eseguire nel caso normale (o nei casi normali) si potrebbero trovare immerse (e "disperse") in mezzo a una quantità preponderante di istruzioni dedicate alla gestione di anomalie (magari molto improbabili), ovviamente a scapito della [[leggibilità (software)|leggibilità]] del programma stesso.
== Segnalazione del fallimento di un metodo ==
Ogni [[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).
Riga 37:
La semantica dell'operatore <code>throw</code> ha alcuni aspetti in comune con quella dell'operatore <code>return</code>; in particolare, l'esecuzione dell'istruzione <code>throw</code> comporta la terminazione immediata del metodo e il passaggio del [[flusso di controllo|controllo]] (seppure secondo un particolare insieme di regole che si esamineranno nel seguito) al chiamante del metodo stesso.
== Gestione dell'eccezione nel chiamante ==
Quando un metodo ne invoca un altro e quest'ultimo può sollevare un'eccezione (come specificato dalla clausola <code>throws</code> della sua intestazione), il chiamante ''può'' predisporsi per gestire tale evenienza. La gestione dell'eccezione avviene utilizzando una [[struttura di controllo]] specifica, detta ''blocco try-catch''. Come si vedrà, questa struttura di controllo ha un funzionamento in parte simile a (una forma ristretta di) [[Struttura di controllo#Goto|''goto'']] e in parte simile alla chiamata di un metodo.
Riga 67:
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).
== 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:
Riga 83:
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.
=== Osservazioni ===
Nei linguaggi sprovvisti di un meccanismo di exception handling, un metodo segnala il proprio fallimento, di norma, [[valore tornato (programmazione)|ritornando]] un valore speciale, a cui il programmatore attribuisce ''convenzionalmente'' il significato di segnalazione di fallimento. Per esempio, il metodo ''differenzaDate'' potrebbe tornare "-1" in caso di fallimento. Questo modello di gestione delle anomalie ha però diverse controindicazioni:
* la segnalazione segue una convenzione che deve essere documentata accuratamente; in assenza di una documentazione appropriata, il chiamante potrebbe non riuscire a interpretare il valore tornato;
Riga 89:
* in ogni caso, non esiste alcun vincolo che ''imponga'' al chiamante di verificare correttamente se il metodo chiamato ha fallito e, nel caso, prendere provvedimenti. Rendendo obbligatoria la gestione delle eccezioni ("handle or declare") il modello di Java impedisce alle anomalie di passare "inosservate". Se questo comporta un onere per il programmatore (proprio perché lo obbliga a gestire ''ogni'' possibile anomalia), d'altra parte la regola contribuisce alla maggiore [[robustezza (software)|robustezza]] del programma.
== Eccezioni come oggetti ==
Negli 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 [[tipo primitivo|tipi primitivi]] come valori-eccezione; le eccezioni, cioè, ''devono'' essere oggetti. Più in particolare, le classi definite per rappresentare le eccezioni devono estendere la classe <code>Throwable</code> (letteralmente: "che può essere lanciato"). A parte questo vincolo, la definizione di una classe di eccezione è libera. Molto spesso, in particolare, si usano [[attributo (programmazione)|attributi]] e [[metodo (programmazione)|metodi]] per corredare l'oggetto-eccezione di informazioni specifiche sul tipo di errore verificatosi.
Riga 143:
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:
Riga 166:
</source>
== Eccezioni e polimorfismo ==
Se le eccezioni sono descritte da classi che implementano ''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).
Riga 203:
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.
== Classificazione delle eccezioni ==
Sebbene al programmatore sia consentito scrivere proprie classi di eccezione, Java dispone già di una propria gerarchia di classi-eccezione, di cui è rilevante conoscere la struttura generale.
Riga 210:
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).
== Voci correlate ==
* [[Gestione delle eccezioni in C plus plus|Gestione delle eccezioni in C++]]
|