In questa lezione • Ges0one delle eccezioni – Procedure parziali – Sollevare e ges0re le eccezioni – Propagazione delle eccezioni – Eccezioni checked e unchecked – Definire nuove eccezioni – Reflec0ng e masking di eccezioni Procedure “parziali” • Molte procedure (metodi) sono parziali, cioè hanno un comportamento specificato solo per un soHo-­‐insieme del dominio degli argomen0 • Per esempio public class Rettangolo{ public void setBase(int base){ //ha senso solo se base > 0 this.base = base; } … } 2 1 Procedure “parziali” e “robustezza” • Le procedure parziali compromeHono la “robustezza” dei programmi – un programma è “robusto” se, anche in presenza di errori o situazioni impreviste, ha un comportamento ragionevole (o, per lo meno, ben definito) – per le procedure parziali, il comportamento per valori d’ingresso che non soddisfano i vincoli specifica0 è spesso non definito dalla specifica! – per tali valori d’ingresso (“inaHesi”), potrebbero verificarsi errori run-­‐0me o, peggio, comportamen0 impredicibili dell’applicazione • Per oHenere programmi robus0, le procedure dovrebbero sempre essere “totali”!!! 3 Ges0one di errori e situazioni eccezionali • Un metodo parziale deve poter segnalare l’impossibilità di produrre un risultato significa0vo o la propria terminazione scorreHa • Ges0one tradizionale a fronte di errori e situazioni eccezionali: il metodo può – terminare il programma – res0tuire un valore convenzionale che rappresen0 l’errore – res0tuire un valore correHo e portare l’oggeHo o l’intero programma in uno stato "scorreHo" (es. usare un aHributo ERROR) – richiamare una funzione predefinita per la ges0one degli errori 4 2 Problemi (i/iv) • Terminare il programma public void setBase(int base){ if(base <= 0) System.exit(-1); this.base = base; } – è spesso una soluzione troppo dras0ca – a rigore è una scelta che speHa al chiamante e non al chiamato 5 Problemi (ii/iv) • Uso di valori di ritorno convenzionali public boolean setBase(int base){ if(base <= 0) return false; this.base = base; return true; } – può non essere fa]bile • perché la procedura non ha un valore di ritorno (void, oppure si traHa di un costruHore) • o perché qualsiasi valore di ritorno è ammissibile – in generale dà poche informazioni riguardo l’errore incontrato – condiziona il chiamante che deve controllare il valore di ritorno 6 3 Problemi (iii/iv) • Portare il programma in uno stato scorreHo public class Rettangolo{ private boolean ERROR = false; public boolean isErrorState(){ return this.ERROR; } public void setBase(int base){ if(base <= 0) this.ERROR = true; else this.base = base; } – la procedura chiamante potrebbe non accorgersi che il programma è stato portato in uno stato "scorreHo” 7 Problemi (iv/iv) • Uso di una funzione predefinita per la ges0one degli errori public void setBase(int base){ if(base <= 0) ErrorHandler.HandleNonPositiveBase(); else this.base = base; } – diminuisce la leggibilità del programma – centralizza la ges0one degli errori (che speHerebbe al chiamante) 8 4 Ges0one esplicita delle eccezioni • Una procedura/metodo può terminare normalmente (con un risultato valido) o sollevare un’eccezione • Le eccezioni vengono segnalate al chiamante che può ges0rle • Le eccezioni hanno un 0po e dei da0 associa0 che danno indicazione sul problema incontrato • Le eccezioni possono essere definite dall’utente (personalizzazione) 9 Uso delle Eccezioni in Java Sollevare le eccezioni • In Java, per sollevare un'eccezione (esplicitamente) in un punto di un algoritmo, si usa: – Istruzione throw (ATTENZIONE: deve seguita da un (reference a) oggeHo) – L’oggeHo che segue throw è deHo per l’appunto oggeHo eccezione (o più semplicemente eccezione) – L’oggeHo eccezione è un vero oggeHo con da0 e metodi – Il 0po dell’oggeHo eccezione determina il 0po dell’eccezione – Spesso l’oggeHo eccezione viene creato in concomitanza dell’istruzione throw public void setBase(int base){ if(base <= 0) throw new NonPositiveBaseException(); this.base = base; } • Seman0ca (informale) dell’istruzione throw – termina l’esecuzione del blocco di codice che lo con0ene – propaga un'eccezione del 0po specificato 10 5 Uso delle Eccezioni in Java Propagazione di eccezioni public static void main(String args[]){ …//main:blocco1:start; boolean b1 = …; class ClasseA{ if(b1){ public static void metodo1(){ …//main:blocco2:start …//metodo1:blocco1:start boolean b2 = …; ClasseB.metodo2(); if(b2){ …//metodo1:blocco1:end …//main:blocco3”; } } } else{ …//main:blocco4:start class ClasseB{ ClasseA.metodo1(); public static void metodo2(){ …//main:blocco4:end …//metodo2:blocco1:start } boolean b3 = …; …//main:blocco2:end if(b3){ } …//metodo2:blocco2:start"); …//main:blocco1:end boolean b4 = …; } if(b4) throw new NullPointerException(); …//metodo2:blocco2:end } …//metodo2:blocco1:end } } 11 Uso delle Eccezioni in Java CRASH!!! Propagazione di eccezioni public static void main(String args[]){ …//main:blocco1:start; boolean b1 = …; class ClasseA{ if(b1){ public static void metodo1(){ …//main:blocco2:start …//metodo1:blocco1:start boolean b2 = …; ClasseB.metodo2(); if(b2){ …//metodo1:blocco1:end …//main:blocco3”; } } } else{ …//main:blocco4:start class ClasseB{ ClasseA.metodo1(); public static void metodo2(){ …//main:blocco4:end …//metodo2:blocco1:start } boolean b3 = …; …//main:blocco2:end if(b3){ } …//metodo2:blocco2:start"); …//main:blocco1:end boolean b4 = …; } if(b4) throw new NullPointerException(); …//metodo2:blocco2:end } …//metodo2:blocco1:end } } 12 6 Uso delle Eccezioni in Java Ges0re le eccezioni • Un'eccezione può essere caHurata e ges0ta aHraverso il costruHo: try { <blocco di codice che potrebbe sollevare eccezione> } catch(TipoEccezione e) { <codice di ges0one: da eseguire quando si verifica l’eccezione> } • Per esempio unOggettoRettangolo = new Rettangolo(…); n = …; //ottengo un numero qualsiasi dall’utente try{ unOggettoRettangolo.setBase(n); } catch (NonPositiveBaseException e){ //codice per gestire l’eccezione //qui è possibile usare l’oggetto e } 13 Uso delle Eccezioni in Java Flusso con ges0one di eccezioni class ClasseA{ public static void metodo1(){ public static void main(String args[]){ try{ …//main:blocco1:start; …//metodo1:blocco1:start boolean b1 = …; ClasseB.metodo2(); if(b1){ …//metodo1:blocco1:end …//main:blocco2:start } catch(NullPointerException e){ boolean b2 = …; …//gestione eccezione if(b2){ } …//main:blocco3”; } } } else{ …//main:blocco4:start class ClasseB{ ClasseA.metodo1(); public static void metodo2(){ …//main:blocco4:end …//metodo2:blocco1:start } boolean b3 = …; …//main:blocco2:end if(b3){ } …//metodo2:blocco2:start"); …//main:blocco1:end boolean b4 = …; } if(b4) throw new NullPointerException(); …//metodo2:blocco2:end } …//metodo2:blocco1:end } 14 } 7 Propagazione delle eccezioni Uso delle Eccezioni in Java • Se durante l’esecuzione di un metodo si verifica un'eccezione: – si termina l’esecuzione del blocco di codice in cui si è verificata l’eccezione e... – se il blocco di codice corrente è un blocco try/catch, si passa il controllo al primo dei rami catch in grado di ges0re l’eccezione, altrimen0... – si risalgono eventuali blocchi di codice più esterni fino a trovare un blocco try/ catch che contenga un ramo catch che sia in grado di ges0re l’eccezione, altrimen0... – l’eccezione viene propagata nel contesto del metodo chiamante… – la propagazione con0nua aHraverso blocchi e metodi fino a che • si trova un blocco try/catch che ges0sce l’eccezione • il programma termina • NB: Se l’eccezione viene ges0ta da un blocco try catch, successivamente all’esecuzione del codice del blocco, il programma con0nua dal comando successivo al blocco stesso 15 Catch con clausole mul0ple • Più clausole catch possono seguire lo stesso blocco try public static void metodo1(){ try{ …//metodo1:blocco1:start ClasseB.metodo2(); …//metodo1:blocco1:end } catch(NullPointerException e){ …//gestione eccezione } catch(UnAltraException e){ …//gestione eccezione } } • Un ramo catch(Ex e) può ges0re un’eccezione di 0po T – se T è di 0po Ex – o se T è un soHo0po di Ex 8 Uso delle Eccezioni in Java Classi Java per le eccezioni Un errore indica che sono occorsi seri problemi durante l’esecuzione dell’applicazione. Non è quindi opportuno né obbligatorio caHurare l’errore TuHe le eccezioni che possono essere lanciate dalla JVM sono specializzazioni della classe Throwable Eccezioni Checked Occorre caHurare tuHe le eccezioni figlie della classe Excep0on ad esclusione della soHoclasse Run0meExcep0on Eccezioni Unchecked Sono eccezioni che possono essere lanciate dalla JVM durante il normale svolgimento delle istruzioni. Un metodo che può lanciare questo 0po di eccezione non è costreHo a dichiararlo nella sua intestazione 17 Uso delle Eccezioni in Java Eccezioni checked • TuHe le eccezioni derivate da java.lang.Excep0on (NB: ad eccezione di quelle che estendono java.lang.Run0meExcep0on) • In Java, tu] i metodi che possono terminare sollevando un’eccezione checked DEVONO dichiarare l’eccezione nell’interfaccia del metodo, usando clausola “throws” public void setBase(int base) throws NonPositiveBaseException { if(base <= 0) throw new NonPositiveBaseException(); this.base = base; } 18 9 Uso delle Eccezioni in Java Eccezioni checked • Devono essere dichiarate dai metodi che possono sollevarle (altrimen0 si ha un errore a compile-­‐0me) • Quando un metodo M1 invoca un altro metodo M2 che può sollevare un’eccezione di 0po Ex (checked), una delle due seguen0 affermazioni deve essere vera (altrimen0 di ha un errore a compile 0me): – l’invocazione di M2 in M1 avviene internamente ad un blocco try/catch che ges0sce eccezioni di 0po Ex (quindi, M1 ges0sce l’eventuale eccezione) – il 0po Ex (o un suo sopra-­‐0po) fa parte delle eccezioni dichiarate nella clausola throws della procedura M1 (quindi, M1 propaga l’eventuale eccezione) 19 Uso delle Eccezioni in Java Eccezioni unchecked • Possono propagarsi senza essere dichiarate in nessuna interfaccia di metodo e senza essere ges0te da nessun blocco try/catch • Molte eccezioni predefinite sono di 0po unchecked – – – – ClassCastExcep0on NullPointerExcep0on ArrayOutOfBoundExcep0on … • NB: Rimane comunque possibile (anche se non è obbligatorio) ges0re le eccezioni unchecked con blocchi try/catch 20 10 Uso delle Eccezioni in Java Definizione di nuove eccezioni • Gli ogge] di un qualunque 0po T definito dall’utente possono essere usa0 per sollevare e propagare eccezioni, a condizione che – T sia definito come soHo-­‐0po della classe Excep0on (o eventualmente Run0meExcep0on) • La definizione della classe che descrive un’eccezione non differisce dalla definizione di una qualsiasi classe definita dall’utente – In par0colare può possedere aHribu0 e metodi propri (usa0 per fornire informazioni aggiun0ve al gestore dell’eccezione) • Convenzione Java per i nomi delle eccezioni – Sebbene sia possibile scegliere liberamente i nomi delle nuove classi che rappresentano nuove eccezioni, la convenzione Java è che tu] i nomi delle eccezioni terminino con la parola Excep0on – Esempio: Scegliete NotFoundExcep0on piuHosto che NotFound 21 Definizione di nuove eccezioni Uso delle Eccezioni in Java Definizione: public class NewTypeOfException extends Exception{ public NewKindOfException(){ super();} public NewKindOfException(String s){ super(s); } } Uso: throw new NewKindOfException(“problema!!!”); Ges0one: try{ .... } catch(NewKindOfException e){ String s = e.toString( ); System.out.printls(s); } 22 11 Il ramo finally Uso delle Eccezioni in Java • Un blocco try/catch può avere un ramo finally in aggiunta a uno o più rami catch • Il ramo finally è comunque eseguito – sia che all’interno del blocco try non vengano sollevate eccezioni – sia che all’interno del ramo try vengano sollevate eccezioni. (Il ramo finally viene eseguito dopo il ramo catch che ges0sce l’eccezione) try { ... } catch(Exception1 e) { //gestione dell’eccezione Exception1 } catch(Exception2 e) { //gestione dell’eccezione Exception2 } finally { //codice sempre eseguito } 23 Programmare con le eccezioni: reflec0ng • In alcuni casi: La ges0one dell’eccezione comporta la propagazione di un’ulteriore eccezione (dello stesso 0po o di 0po diverso) public int min(int[] a) throws EmptyException { int m; try { m = a[0]; } catch (IndexOutOfBoundsException e){ throw new EmptyException(“Array is empty”); } for (int i = 1; i < a.length; i++) if (a[i] < m) m = a[i]; return m; } 24 12 Programmare con le eccezioni: masking • In alcuni casi: Dopo la ges0one dell’eccezione, l’esecuzione con0nua seguendo il normale flusso del programma public boolean sortedAscending (int[] a) { int previous; try { previous = a[0]; for (int i=1; i < a.length; i++) { if (previous <= a[i]) previous = a[i]; else return false; } }catch (IndexOutOfBoundsException e){ System.out.println(“special: empty => sorted”); } return true; } 25 ProgeHare le eccezioni • Uso delle eccezioni – ges0one dei casi in cui le precondizioni di un metodo non sono soddisfaHe dal chiamante – ges0one della codifica di informazioni par0colari nei risulta0 delle procedure – realizzazione di procedure più generali e riusabili • Le eccezioni unchecked dovrebbero essere evitate il più possibile. Il loro uso dovrebbe essere limitato ai casi in cui – c’è un modo conveniente e poco costoso di evitare l’eccezione (per gli array, le eccezioni di 0po OutOfBoundExcep0on possono essere evitate controllando in an0cipo il valore dell’aHributo length dell’array) – l’eccezione è usata solo in un contesto ristreHo 26 13 ESERCIZI: GESTIONE ECCEZIONI Eccezioni: esercizio 1 • Considerare un generico programma Java in cui ci siano due classi in una gerarchia di ereditarierà, e un metodo definito nella classe più specializzata • Usando solo reference del 0po più generale, creare ogge] delle due classi e almeno un reference null, quindi effeHuare dei downcast esplicito di tu] i reference e invocare il metodo. Osservare in quali casi si ha un’eccezione ClassCastExcep0on o NullPointerExcep0on • Creare un blocco che ges0sce le eccezioni aHraverso catch in cascata per NullPointerExcep0on, ClassCastExcep0on e Excep0on, con un blocco finally in coda. • Osservare il comportamento delle eccezioni modificando l’esempio in vari modi 14