Interfacce funzionali built-in (java.util.function)

  • Predicate <T>: funzione a valori booleani a un solo argomento generico T
Predicate <String> predicate = s -> s.lenght()>0;
predicate.test("foo");//true
predicate.negate().test("foo"); //flase
Predicate<String> predicate2 = s -> s.startsWith("f");
predicate.and(predicate2).test("foo"); // true
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
  • Function <T,R> : funzione a un argomento di tipo T e un tipo di ritorno R entrambi generici
Function<String, Integer> toInteger = Integer::valueOf;
Integer k = toInteger.apply("123");
Function<Integer, Integer> square = k -> k*k;
Integer sqr = square.apply(5); // 25
  • Supplier <String> : funzione senza argomenti input
Supplier <String> stringSupplier = () -> "ciao";
  • Supllier<T> : funzione senza argomenti in input
Supplier <Person> personSuplier = Person::new;
personSupplier.get(); //new Person()

Warning

Possiamo ottenere un riferimento al costruttore mediante la parola chiave new

  • Consumer <T> : funzione con un argomento di tipo generico T e nessun tipo di ritorno
Consumer<Person> greeter1 = p -> System.out.println("Hello," + p.firstName);
greeter1.accept(new Person("Luke", "Skywalker"));
Consumer<Person> greeter2 = System.out::println;

For each su java.util.Collection

  • Le collection sono ora dotate di un metodo forEach che prende in input un’interfaccia Consumer <? super T> dove T è il tipo generico della collection
Collection<String> c = Arrays.asList(ʺaaʺ, ʺbbʺ, ʺccʺ);
// In Java 7:
for (String s : c) System.out.println(s);
// In Java 8:
c.forEach(s -> System.out.println(s));
// persino meglio:
c.forEach(System.out::println);

Pila e Coda

Coda

  • Esistono implementazioni standard della coda mediante l’interfaccia Queue
    • LinkedList implementa l’interfaccia Queue
  • Operazioni principali
    • add : inserisce un elemento in coda
    • remove : rimuove un elemento dall’inizio della coda
    • peek : restituisce l’elemento all’inizio della coda senza rimuoverlo

Pila

  • Esiste un’implementazione standard mediante la classe Stack
    • Implementa l’interfaccia List
  • Operazioni principali
    • push: inserisce un elemento in cima alla pila
    • pop: rimuove l’elemento in cima alla pila
    • peek: restituisce l’elemento in cima alla pila senza rimuoverlo

Gli alberi

  • Una struttura dati ricorsiva in cui ogni nodo possiede un padre tranne la radice

  • Gli alberi più comuni sono binari (ovvero con al più due figli per nodo)

  • Uso di classe annidata (interna se serve il riferimento all’albero) per rappresentare il nodo di un albero binario:

public class BinaryTree
{
	//radice dell'albero
	private Nodo root;
 
	static public class Nodo
	{
		private Nodo left;
		private Nodo right;
		private int valore;
 
		public Nodo (Nodo left, Nodo right, int valore)
		{
			this.left = left;
			this.right = right;
			this.valore = valore;
		}
	}
}

Ricercare un’informazione all’interno dell’albero

public class BinaryTree
{
	//radice dell'albero
	private Nodo root;
 
	static public class Nodo
	{
		private Nodo left;
		private Nodo right;
		private int valore;
 
		public Nodo (Nodo left, Nodo right, int valore)
		{
			this.left = left;
			this.right = right;
			this.valore = valore;
		}
	}
}
 
public boolean contains (int val)
{
	return contains(root, val);
}
 
private boolean contains (Nodo n, int val)
{
	if (n==null) return false;
	if (n.valore == val) return true;
	return contains(n.left, val) || contains(n.right, val);
}

Eccezioni

  • Sono un meccanismo utile a notificare e gestire gli errori
  • Indica che durante l’esecuzione si è verificato un errore
  • Il termine “eccezione” indica un comportamento anomalo, che si discosta dalla normale esecuzione
  • Codice più robusto e sicuro

Scorrere una lista in java

int[] estrazioneLotto = {3, 29, 10 ,23, 67};
 
for (int i=0; i<=5; i++) System.out.println(estrazioneLotto[i] + " ");
  • Ma in questo caso la JVM solleva questa eccezione:
Excetion in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
  • Simile all’out of Index in Python

Convertire da Stringa a intero

  • Mediante il metodo statico Integere.parseInt
  • Ma se la stringa non contenesse un intero:
String x = "abc";
int k = Integer.parseInt(x);
  • Eccezione:
java.lang.NumberFormatException: For input string: "x"

Dividere per 0

int x = 5;
int y = 0;
 
int z = x/y;
  • Eccezione:
java.lang.ArithmeticException: / by zero

Vantaggi delle eccezioni

  • In linguaggi come C, la logica del programma e la logica di gestione degli errori sono interlacciate: questo rende più difficile leggere. modificare e mantenere il codice
  • Gli errori vengono propagati verso l’alto lungo lo stack di chiamate
  • Codice più robusto: non dobbiamo controllare esaustivamente tutti i possibili tipi di errore: il polimorfismo lo fa per noi, scegliendo l’intervento più opportuno

Svantaggi delle eccezioni

  • L’onere di gestire i vari tipi di errore si sposta sulla JVM che si incarica di capire il modo più opportuno per gestire la situazione di errore

Che cosa si può gestire con le eccezioni?

  • Errori sincroni, che si verificano a seguito dell’esecuzione di un’istruzione
    • Errori non critici: errori che derivano da condizioni anomale
      • Divisione per zero
      • Errore di I/O
      • Errori durante il parsing
    • Errori critici o irrecuperabili: errori interni alla JVM
      • conversione di tipo non consentito
      • accesso ad una variabile riferimento con valore null
      • mancanza di memoria libera

Che cosa non si può gestire con le eccezioni?

  • Eventi asincroni
    • completamenti nel trasferimento I/O
    • ricezione messaggi su rete
    • click del mouse
  • Eventi che accadono parallelamente all’esecuzione e quindi indipendenti dal flusso di controllo

Blocco try-catch

  • Consente di catturare le eccezioni
try{
Exceptions...
}catch (Exception e){
//Do nothing
}

Blocco try

  • Si inseriscono tutte le istruzioni dalle quali vengono sollevate le eccezioni che vogliamo catturare

Blocco catch

  • E’ necessario indicare il tipo di eccezione da catturare e specificare nel blocco le azioni da attuare a seguito dell’eccezione sollevata

L’ordine conta nel blocco catch

  • E’ molto importante considerare l’ordine con cui si scrivono i diversi blocchi catch e catturare le eccezioni dalla più specifica a quella più generale
  • Nell’attuare il processo di cattura, la JVM sceglie il primo catch compatibile, tale cioè che il tipo dell’eccezione dichiarata sia lo stesso o un supertipo dell’eccezione lanciata durante l’esecuzione
  • Spesso vogliamo rispondere ad un’eccezione con il rimedio specifico e non con uno più generale

Catch integrato di eccezioni alternative

  • E’ possibile specificare un’unica clausola catch con diversi tipi di eccezioni utilizzando l’operatore |
    • Utile laddove, a fronte di eccezioni diverse, si debba avere un comportamento analogo
    • Disponibile da Java 7
try
{
 
if (condizione) throw new Eccezione1();
else throw new Eccezione2();
 
}
catch(Eccezione1|Eccezione2 e)
{
 
// gestione dei due casi in un unico blocco
 
}

Flusso in presenza o assenza di eccezioni

  • Se durante l’esecuzione non vengono sollevate eccezioni:
    • Ciascuna istruzione all’interno del blocco try viene eseguita normalmente
    • Terminato il blocco try, l’esecuzione riprende dalla prima linea dopo il blocco try-catch
  • Se viene sollevata un’eccezione:
    • L’esecuzione del blocco try viene interrotta
    • Il controllo passa al primo blocco catch compatibile, tale cioè che il tipo dichiarato nella clausola catch sia dello stesso tipo dell’eccezione sollevata, o un suo super-tipo
    • L’esecuzione riprende dalla prima linea dopo il blocco try-catch

Politica del catch-or-declare

  • Una volta sollevata un’eccezione, possiamo:
    • Ignorare l’eccezione e propagarla al metodo chiamante, a patto di aggiungere all’intestazione del metodo la clausola throws, seguito dall’elenco delle eccezioni potenzialmente sollevate(declare)
    • Catturare l’eccezione, ovvero gestire la situazione anomala in modo opportuno, prendendo provvedimenti e contromisure atte ad arginare il più possibile la situazione di emergenza (catch)
  • Se il requisito catch-or-declare non viene soddisfatto il compilatore emette un errore che indica l’eccezione dev’essere catturata o dichiarata
  • Questo serve a forzare il programmatore a considerare i problemi legati all’uso di metodi che emettono eccezioni

Metodi printStackTrace() e getMessage()

  • Quando un’eccezione non viene mai catturata, l’effetto è il seguente:

  • Su schermo viene stampato un ‘riassunto’ associato all’eccezione non catturata, chiamato stack trace
  • Questo riporta:
    • Il thread in cui l’eccezione è stata sollevata
    • Il nome dell’eccezione sollevata
    • La successione, in ordine inverso di invocazione, dei metodi coinvolti
    • Il file sorgente e il numero di riga
  • L’output generato a schermo da un’eccezione non catturata è prodotto dal metodo printStackTrace(), offerto dalla classe Throwable
  • Un altro metodo messo a disposizione dalla stessa classe è getMessage(), in grado di restituire, se prevista o disponibile, una della descrizione sintetica ragione per la quale si è verificata l’eccezione

Creare eccezioni personalizzate

  • E’ possibile definire delle eccezioni personalizzate
  • In questo modo i messaggi di errore conservano la semantica legata all’applicazione
  • Al momento della creazione di un nuovo tipo di eccezione sarà opportuno studiarne la natura e lo scopo (errore sincrono/asincrono, possibilità di recovery o errore irreversibile…) e scegliere la super-classe più adeguata

Come si crea?

public class NonToccareLaMiaRobaaException extends Exception
{
 
}

La parola chiava Throw

  • Tramite la parola chiave throw è possibile sollevare (o lanciare) una nuova eccezione

L’importanza di creare eccezioni personalizzate

  • Immaginiamo che venga invocato il metodo getLibro(50)
  • Viene sollevata l’eccezione personalizzata LibroMancanteException,
  • In assenza di questa sarebbe stata sollevata l’eccezione IndexOutOfBoundsException...
  • Che significato specifico trasmette IndexOutOfBoundsException ad un utilizzatore di una libreria? Nessuno!
  • L’eccezione personalizzata, invece, nasconde i dettagli implementativi e trasmette un significato appropriato al contesto

Blocco finally

  • Tipicamente all’interno del blocco finally vengono eseguite operazioni di clean-up (es. chiusura di eventuali file aperti o rilascio di risorse) in modo da garantire un certo stato dell’esecuzione

Classe Throwable

  • Implementa il concetto di eccezioni ed estende direttamente la classe Object
  • Gli oggetti di tipo Throwable sono gli unici che è possibile utilizzare con il meccanismo delle eccezioni

Gerarchia di eccezioni in Java

  • Come dirette sottoclassi di Throwable troviamo la classe Error e la classe Exception

Le classi Exception ed Error

Exception

  • Eccezioni interne alla JVM (classe RuntimeExcdption): legate ad errori nella logica del programma
  • Eccezioni regolari (Ex. IOException, ParseException, TimeoutExceptio): errori che le applicazioni dovrebbero anticipare e dalle quali poter riprendersi

Error

  • Cattura l’idea di condizione eccezionale irrecuperabile
    • Assai rari e non dovrebbero essere considerati dalle applicazioni (ThreadDeath, OutOfMemoryError)

Eccezioni di tipo Checked

  • È sempre necessario attenersi al paradigma catch-or-declare
    • Sono eccezioni comuni, ovvero quelle che estendono Exception (ma non RuntimeException)
    • Esempi: ParseException, ClassNotFoundException, FileNotFoundException

Eccezioni di tipo Unchecked

  • Non si è obbligati a dichiarare le eccezioni sollevate o a catturarle in un blocco try-catch (ma è possibile farlo)
  • Sono eccezioni che estendono Error o RuntimeException
  • Esempi: IndexOutOfBoundsException, ClassCastException, NullPointerException, ArithmeticException, OutOfMemoryError