Nuove eccezioni
Abbiamo esaminato finora la gerarchia delle eccezioni in Java e le modalità di utilizzo delle
eccezioni già definite e rese disponibili dal linguaggio stesso.
Ma se volessimo definire ed utilizzare delle nostre eccezioni
personalizzate? Magari specifiche
per determinate problematiche, che potrebbero emergere nell'applicazione che stiamo realizzando?
In tali situazioni, il programmatore può definire nuove
eccezioni.
Per fare ciò è necessario creare una nuova classe che derivi (o
specializzi) dalla classe
Exception.
Vediamo nel dettaglio cosa vuol dire creare una nuova eccezione in linguaggio Java.
Supponiamo di voler definire una nostra classe per trattare le anomalie di divisione per zero.
Dobbiamo definire una classe, che possiamo chiamare DivisoreNulloException, che deve
specializzare la classe Exception.
Per indicare che una classe ne specializza un'altra, in Java occorre utilizzare, nella
definizione della classe, la parola chiave
extends, seguita dalla classe che
viene specializzata.
Osservare anche l'utilizzo del suffisso Exception (per convenzione, comprensione e leggibilità del codice) nell'attribuzione del nome alla nuova classe eccezione.
L'esempio che segue mostra il codice da scrivere in Java.
// notare l'uso della parola chiave extends
// per estendere la classe Exception
public class DivisoreNulloException extends Exception {
}
Fatto ciò, possiamo ora utilizzare la nuova tipologia di
eccezione creata, in una nostra funzione
di divisione tra due numeri interi.
Ma cosa si intende precisamente con il termine utilizzare?
Utilizzare una tipologia di eccezione vuol dire sollevare una istanza
specifica della classe
eccezione, quando si verificano determinate condizioni.
Per sollevare una istanza di eccezione, nel linguaggio Java, occorre utilizzare la parola chiave
throw.
Inoltre, ogni volta che una funzione solleva una eccezione, essa è obbligata, in Java, a
dichiararlo nella sua definizione, tramite la parola chiave throws.
Ritorneremo a parlare dell'utilizzo della parola chiave throws
quando approfondiremo il principio handle or declare.
In codice Java, quanto detto viene implementato come mostrato di seguito:
// notare l'uso di throws nella definizione della funzione
// per comunicare che la funzione potrebbe,
// al verificarsi di determinate condizioni,
// sollevare l'eccezione DivisoreNulloException
public static int quoziente(int dividendo, int divisore) throws DivisoreNulloException {
if (divisore == 0) { // se si verifica la condizione
// crea l'istanza dell'eccezione e la solleva utilizzando throw
throw new DivisoreNulloException();
}
return dividendo / divisore;
}
L'esecuzione dell'istruzione throw interrompe il flusso delle istruzioni della funzione in cui è invocata e restituisce il controllo alla funzione chiamante, che deve gestirla attraverso l'uso del blocco try-catch.
Mettiamo ora insieme quanto esaminato fino a questo punto e, supponendo di definire la nuova
tipologia di eccezione (DivisoreNulloException) esattamente come visto in precedenza, quindi
semplicemente derivando dalla classe Exception, senza altro codice, riscriviamo il programma per
il calcolo del quoziente della divisione tra due numeri interi richiesti all'utente.
Il codice in Java potrebbe essere quello che segue:
import java.util.Scanner;
public class CalcoloQuoziente {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.print("Inserisci il dividendo: ");
int dividendo = sc.nextInt();
System.out.print("Inserisci il divisore: ");
int divisore = sc.nextInt();
String msg = "";
try {
int quoziente = quoziente(dividendo, divisore);
// se non viene sollevata nessuna eccezione
// costruisco il messaggio della divisione
msg = "Il quoziente della divisione tra " + dividendo +
" e " + divisore + " è " + quoziente;
} catch(DivisoreNulloException ex) {
// se viene sollevata l'eccezione di DivisoreNulloException
// costruisco il messaggio di errore
msg = "Errore nella divisione: "+ex.getMessage();
} finally {
// stampo il messaggio costruito in precedenza
System.out.println(msg);
}
}
public static int quoziente(int dividendo, int divisore) throws DivisoreNulloException {
if (divisore == 0) {
throw new DivisoreNulloException();
}
return dividendo / divisore;
}
}
L'output dell'esecuzione del programma, nel caso in cui l'utente inserisca i valori 10, come dividendo, e 0 come divisore, è il seguente:
Inserisci il divisore: 0
Errore nella divisione: null
Notiamo che, in assenza di codice nella classe
DivisoreNulloException, il programma segnala
correttamente l'anomalia, ma il suo messaggio descrittivo non è
definito, quindi l'esecuzione
visualizza il valore null, quando viene chiesto di stampare
l'esito del metodo getMessage.
Se modifichiamo il codice della classe DivisoreNulloException,
definendo il comportamento del
metodo getMessage, come mostrato nel codice
seguente, senza cambiare nulla nella classe CalcolaQuoziente,
l'output dell'esecuzione diventa quello mostrato di seguito.
public class DivisoreNulloException extends Exception {
// definiamo il comportamento del
// metodo getMessage
@Override
public String getMessage() {
return "Divisore nullo!";
}
}
Inserisci il divisore: 0
Errore nella divisione: Divisore nullo!
Il metodo getMessage è definito nella classe Exception, ma, come abbiamo appena visto, restituisce null, per cui per modificarne il comportamento occorre ridefinirlo, ossia fare un override. Per questo motivo, come abbiamo già visto per il metodo toString, quando viene ridefinito il metodo getMessage occorre indicare l'etichetta @Override.
Facciamo ancora un passo oltre.
Se volessimo che il messaggio restituito dalla classe DivisoreNulloException, attraverso il
metodo getMessage sia:
Impossibile eseguire la divisione tra 10 e 0, il divisore è
nullo!
dovremmo, in qualche modo, comunicare alla classe eccezione quali sono i valori per i quali si
sta
cercando di calcolare il quoziente.
Potremmo, ad esempio, utilizzare il costruttore della classe eccezione per passare tali
informazioni.
Il codice della classe DivisoreNulloException diventera allora il seguente:
public class DivisoreNulloException extends Exception {
private int dividendo;
public DivisoreNulloException(int dividendo) {
this.dividendo = dividendo;
}
// definiamo il comportamento del
// metodo getMessage
@Override
public String getMessage() {
return "Impossibile eseguire la divisione tra " +
dividendo + " e 0", il divisore è nullo!";
}
}
Questa volta, però, dobbiamo fare una piccola modifica al codice della classe CalcolaQuoziente
perché, nel momento in cui la funzione, incaricata di eseguire la divisione tra dividendo e
divisore, rileva la condizione di divisore nullo, deve comunicare, attraverso il costruttore
della classe DivisoreNulloException il valore del dividendo.
Il codice della classe CalcolaQuoziente verrebbe modificato come segue:
import java.util.Scanner;
public class CalcoloQuoziente {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.print("Inserisci il dividendo: ");
int dividendo = sc.nextInt();
System.out.print("Inserisci il divisore: ");
int divisore = sc.nextInt();
String msg = "";
try {
int quoziente = quoziente(dividendo, divisore);
// se non viene sollevata nessuna eccezione
// costruisco il messaggio della divisione
msg = "Il quoziente della divisione tra " + dividendo +
" e " + divisore + " è " + quoziente;
} catch(DivisoreNulloException ex) {
// se viene sollevata l'eccezione di DivisoreNulloException
// costruisco il messaggio di errore
msg = "Errore nella divisione: "+ex.getMessage();
} finally {
// stampo il messaggio costruito in precedenza
System.out.println(msg);
}
}
public static int quoziente(int dividendo, int divisore) throws DivisoreNulloException {
if (divisore == 0) {
// notare il passaggio dell'informazione
// del dividendo alla classe
// DivisoreNulloException
// attraverso il suo costruttore
throw new DivisoreNulloException(dividendo);
}
return dividendo / divisore;
}
}
Se mandiamo in esecuzione l'applicazione così trasformata, l'output, in caso di valori di dividendo e divisore pari a 10 e 0, è il seguente:
Inserisci il divisore: 0
Impossibile eseguire la divisione tra 10 e 0, il divisore è nullo!