# Eccezioni

### Introduzione eccezioni

In aggiunta alle normali strutture di controllo del flusso di esecuzione (**if**, **while**, **for** ...) del programma, Java ha un modo per gestire i casi "eccezionali"  che possono deviare il normale flusso di esecuzione. Quando un' eccezione avviene durante l' esecuzione del programma, il comportamento di default è di terminare il programma e stampare un messagggio d'errore. Tuttavia Java permette anche di "catturare" (**catch**) le eccezioni e rispondere in modo differente dal semplice terminazione improvvisa del programma. Questo è ottenuto con lo statement **try...catch** .

Le eccezioni sono degli oggetti sottoclasse della classe base **Throwable** e sono orgazinnate in due sottoclassi **Error** ed **Exception**. Nella figura alcuna delle eccezioni standard del JDK:

![Gerarchia classi delle eccezioni](https://3264574854-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LVU7WiFKajQhwsnAlGh%2F-L_6sYGN3VJ766Rzhy0a%2F-L_6tT3_tEhC6usXem0-%2FEccezioni.PNG?alt=media\&token=5f7879c9-83c7-4bd3-9111-d2cc6eb12794)

Sottoclassi di **java.lang.Error** sono errori nel funzionamento del programma all'interno dell'interprete Java durante il funzionamento (es esaurimento memoria *java.lang.OutOfMemoryException* ); sono errori gravi, che implicano il termine del programma. Essendo legati al funzionamento del programma non possono essere recuperati, il programma va in uno stato in cui l'unica cosa che può fare è terminale l'applicazione (in questo caso è la cosa migliore da fare). Altri tipi d'errore, le eccezioni, sottoclassi di **java.lang.Exception**, possono essere invece recuperate e vedremo come.

### Esempi eccezioni

Vediamo come utilizzando `Integer.parseInt(String value)` che può lanciare l'eccezione `java.lang.NumberFormatException` nel caso la stringa di input non sia possibile convertirla in intero (es input "pippo"):

```java
package tryexception;

import java.util.Scanner;

/**
 * Esempio programma in cui l'eccezione non è catturata per gestirla.
 * Es: l'input "fred" causerebbe eccezione del metodo Integer.parseInt()
 * @author cam
 */
public class TestExceptionReadInt {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		System.out.println("Digita un intero:");
		
		String line = in.next();
		int intValue = Integer.parseInt(line);
	    
		System.out.println("Valore: " + intValue);
		in.close();

	}

}

```

Ora utilizzando `try...catch` per gestire l'eccezione:

```java
package tryexception;

import java.util.Scanner;

/**
 * Eccezione gestita tramite try...catch
 * 
 * @author cam
 *
 */
public class TestExceptionReadIntWithCatch {

	public static void main(String[] args) {
		
		Scanner in = new Scanner(System.in);
		System.out.println("Digita un intero:");
		
		String line = in.next();
		
		try {
			int intValue = Integer.parseInt(line);
	    	System.out.println("Valore: " + intValue);
		} catch (NumberFormatException e) {
			System.out.println(e.getMessage());
		}
		
		System.out.println("DOPO CATCH");
		in.close();
		
	}

}

```

Anche il metodo `nextInt()` di `Scanner` può lanciare un'eccezione `java.util.InputMismatchException` in caso in cui l'input non sia convertibile in intero.

```java
package tryexception;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * Esempio cattura dell'eccezione java.util.InputMismatchException 
 * 
 * @author cam
 *
 */
public class TestExceptionReadIntWithFinally {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		System.out.println("Digita un intero:");
		
		try {
			int intValue = in.nextInt();
			System.out.println("Valore: " + intValue);
		} catch (InputMismatchException e) {
			System.out.println("Exception message: " + e.getMessage());
		} finally {
			System.out.println("SONO IN FINALLY!!!");
			in.close();
		}
	}

}

```

Nell'esempio sopra vediamo l'utilizzo di `try...catch ...finally`. La parte in **finally** è sempre eseguita sia che sia lanciata un'eccezione nel blocco `try...catch` sia che non ci siano eccezioni.&#x20;

Nell'esempio sotto vediamo che esiste la forma `try...finally` in cui non viene catturata l'eccezione (non c'è **catch**) ma comunque viene garantita l'esecuzione sempre del blocco **finally**, sia in caso di nessuna eccezione ma anche quando nel blocco **try** viene lanciata un'eccezione.

```java
package tryexception;

import java.util.Scanner;

/**
 * Esempio per evidenziare l'utilizzo di finally nella gestione delle eccezioni, 
 * senza il catch.
 * 
 * @author cam
 *
 */
public class TestExceptionReadIntOnlyFinally {

	public static void main(String[] args) {
		
		Scanner in = new Scanner(System.in);
		
		System.out.println("Digita un intero:");
		
		try {
			int intValue = in.nextInt();
		
			System.out.println("Valore: " + intValue);
		} finally {
			System.out.println("SONO IN FINALLY!!!");
			in.close();
		}
	}

}

```

Finora abbiamo utilizzato dei metodi che potevano lanciare un' eccezione: es il metdo `parseInt` di `Integer` in caso di input "*fred*" invece di un intero. E se volessi che un mio metodo potesse lanciare un'eccezione al verificarsi di alcune condizioni?&#x20;

Facciamo l'esempio di una classe `Portafoglio` in cui si possono aggiungere soldi e prelevare soldi, se però cerco di prelevare una quantità di denaro che non c'è in cassa, viene lanciata l'eccezione `java.lang.IllegalArgimentException` per segnalare che l'operazione non è consentita.

Vediamo l'esempio:

```java
package tryexception;

class Portafoglio {
	
	private int disponibilita = 0;
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 */
	public int versa(int value) {
		this.disponibilita += value;
		return this.disponibilita;
	}
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 * 
	 * @throws IllegalArgumentException se la disponibilità è < della richiesta di 
	 * prelievo.
	 * 
	 */
	public int preleva(int value) {
		if (this.disponibilita < value) 
			throw new IllegalArgumentException("Disponiblità: " + this.disponibilita 
					+ " < richiesta prelievo: " + value);
		
		this.disponibilita -= value;
		return this.disponibilita;
	}
	
	public int getDisponibilita() {
		return this.disponibilita;
	}
	
}

public class TestPortafoglio {
    	
	public static void main(String[] args) {
		
		Portafoglio portLucia = new Portafoglio();
		
		portLucia.versa(400);
		portLucia.preleva(600);
		
	}

}

```

Notiamo l'utilizzo di **`throw`** per generare l'eccezione all'interno del metodo `preleva` di `Portafoglio`.

Esercizio:

* modificate il codice nel `main` di `TestPortafoglio` per catturare l'eccezione;&#x20;

### Creare una nostra eccezione

Nell'ultimo esempio (quello del `Portafoglio` abbiamo visto come "lanciare" un'eccezione: data la classe della eccezione che voglio generare, creo un istanza della classe e tramite **trow**  lancio l'eccezione. Nell'esempio:&#x20;

```java
throw new IllegalArgumentException("Disponiblità: " + this.disponibilita 
					+ " < richiesta prelievo: " + value);
```

In questo caso ho utilizzato una delle eccezioni della libreria standard del JDK (`java.lang.IllegalArgumentException`).&#x20;

Nulla mi impedisce, qualora lo ritenessi opportuno, creare una mia eccezione e utilizzarla come ho fatto per `java.lang.IllegalArgumentException`.

Vediamo subilto un esempio:

Definiamo una nostra eccezione:

```java
package tryexception;

public class MyException extends RuntimeException {
	
	public MyException(String message) {
		super(message);
	}
}

```

E' una classe normale come qualsiasi altra classe l'unica cosa è che è sottoclasse di `java.lang.RuntimeException`.

Utlizziamola nel nuovo programma e vediamo che non cambia niente rispetto a come è stata utilizzata una delle eccezioni della libreria standard:

```java
package tryexception;

class Portafoglio2_0 {
	
	private int disponibilita = 0;
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 */
	public int versa(int value) {
		this.disponibilita += value;
		return this.disponibilita;
	}
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 * 
	 * @throws MyException se la disponibilità è < della richiesta di 
	 * prelievo.
	 * 
	 */
	public int preleva(int value) {
		if (this.disponibilita < value) 
			throw new MyException("Disponiblità: " + this.disponibilita 
					+ " < richiesta prelievo: " + value);
		
		this.disponibilita -= value;
		return this.disponibilita;
	}
	
	public int getDisponibilita() {
		return this.disponibilita;
	}
	
}

public class TestPortafoglioMyException {
    	
	public static void main(String[] args) {
		
		Portafoglio2_0 portLucia = new Portafoglio2_0();
		
		portLucia.versa(400);
		portLucia.preleva(600);
		
	}

}

```

### Eccezioni da gestire obbligatoriamente

Finora negli esempi abbiamo visto tutte eccezioni che era facoltative che fossero gestite tramite il meccanismo di **try....catch**: se non gestite causavano l'uscita immediata del programma con la print dello stacktrace, il punto, cioè in cui è avvenuta l'eccezione con lo stack delle chiamate.

Le eccezioni sottoclassi di `java.lang.RuntimeException` come `java.lang.NumberFormatException` o j`ava.lang.IllegalArgumentException` e `tryexception.MyException` non obbligano ad essere gestite tramite **try...catch**.

Esiste un altro genere di eccezioni derivate `java.lang.Exception` che Java obbliga a gestire tramite il meccanismo di **try...catch**. Se non si scrivesse il codice per gestire l'eccezione, il compilatore darebbe errore di compilazione.

Vediamo un esempio di eccezione  che deve essere gestita obbligatoriamente e il suo utilizzo nell'esempio del portafoglio:

```java
package tryexception;

public class PortafoglioException extends Exception {
	
	PortafoglioException(String message) {
		super(message);
	}
}
```

```java
package tryexception;

class Portafoglio3_0 {
	
	private int disponibilita = 0;
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 */
	public int versa(int value) {
		this.disponibilita += value;
		return this.disponibilita;
	}
	
	/**
	 * 
	 * @param value
	 * @return la disponibilità ad operazione avvenuta
	 * @throws PortafoglioException se la disponibilità è < della richiesta di 
	 * prelievo.
	 * 
	 */
	public int preleva(int value) throws PortafoglioException {
		if (this.disponibilita < value) 
			throw new PortafoglioException("Disponiblità: " + this.disponibilita 
					+ " < richiesta prelievo: " + value);
		
		this.disponibilita -= value;
		return this.disponibilita;
	}
	
	public int getDisponibilita() {
		return this.disponibilita;
	}
	
}

public class TestPortafoglioWithPortafogliException {
    	
	public static void main(String[] args) {
		
		Portafoglio3_0 portLucia = new Portafoglio3_0();
		
		portLucia.versa(400);
		try {
			portLucia.preleva(600);
		} catch (PortafoglioException e) {
			System.out.println("PortafoglioException: " + e.getMessage());
		}
		
		System.out.println("SONO QUI");
		
	}

}


```

Da notare inoltre nella dichiarazione del metodo `preleva` della classe `Portafoglio3_0` il **throws** (al plurale e non al singolare come invece quando viene lanciata l'eccezione).

Un metodo al cui interno viene lanciata un'eccezione di quelle obbligatorie da essere gestite, ha due possibilità:

* o la gestisce al suo interno tramite il **try...catch**;
* o dichiara con **throws** che il metodo può lanciare l'eccezione. Sarà compito del chiamante gestire l'eccezione o, in modo consapevole, dichiarare a sua volta che anche lui può lanciare (**throws**) l'eccezione;

Il meccanismo utilizzato da Java per le eccezioni e in particolare per quelle **non** di tipo `java.lang.RuntimeException`, obbliga il programmatore a gestire le situazioni di Exception, tramite il **try..catch**, o dichiarando, tramite il **throws**, che il metodo può generare un eccezione.

Esercizi trattati <https://github.com/checksound/ProvaEccezioni>

Finora abbiamo fatto un'introduzione con alcuni esempi, nel seguente tutorial vedremo gli stessi concetti in modo più sistematico: <http://www3.ntu.edu.sg/home/ehchua/programming/java/J5a_ExceptionAssert.html>&#x20;

### ESERCIZIO

Partendo dell'esempio del portafoglio scrivere una nuove versione che controlla che non si siano ritirati più di 500 euro a prelievo. In caso che si superi questo limite, anche se magari sul conto ci sono 2000 euro, lanciare l'eccezione `SingleWithdrawnLimitException`(*withdrawn* = preliev&#x6F;*)*. Costruire un caso per testare questa condizione, simulando il prelievo. La classe `Porfolio` genera inoltre ancora l'eccezione `AmountWithdrawnException` (o `PorfolioException` di prima) in caso in cui l'utente cerchi di prelevare una quantità maggiore di quanto ci sia in cassa: esempio, sul conto ci sono 500 euro e l'utente cerca di prelevare 600 euro, allora il metodo preleva deve generare l'eccezione `AmountWithdrawnException` .

1. Verificate costruendo dei casi in cui verificate il lancio delle le due eccezioni.
2. Scrivete un'ulteriore classe `Porfolio` (potete farlo in un altro package, così conservate le versione precedente) che controlli che il limite del 1500 non sia superato sommando gli ultimi prelievi. Quindi se sul conto ho ancora 2000 euro e tento di ritirare 200 euro ma finora ho già fatto due prelievi da 700 euro ciascuno, con il nuovo prelievo arriverei a 1600 (>1500 limite giornaliero) allora viene lanciata una nuove eccezione `DailyWithdrawnLimitException`.  Serve quindi una variabile che tenga conto della somma dei prelievi precedenti. Scrivete anche un metodo `reset()` della classe `Porfolio` che faccia il reset della somma dei prelievi in modo che quando il giorno passa potete riprendere a prelevare. Scrivete un programma in cui testate il caso di `DailyWithdrawnLimitException` e poi l'utilizzo di reset per simulare il passaggio della giornata e il fatto che potete continuare a prelevare.

![Metodo preleva della classe Porfolio](https://3264574854-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LVU7WiFKajQhwsnAlGh%2F-LcjAWLKFFKMU5MnVmRZ%2F-LcjAp-skJwkXjjSPXkF%2FPorfolio_1.png?alt=media\&token=b472308a-e1cb-49a0-a607-f806b267c906)
