Dispense sulle Espressioni Lambda usate in Java 8 realizzate dall'Ing. Roberto Musa per il Corso di Programmazione ad Oggetti presso la SDSS del Politecnico di Torino di Scano di Montiferro.
2. Cosa sono le Espressioni Lamba
Le espressioni Lambda sono di fatto delle funzioni anonime dove si indica
direttamente, senza assegnare un nome, parametri restituiti e codice della
funzione.
La struttura base di una espressione lambda è il seguente:
([tipoA a, tipoB b,… ])->{Codice…}
Tutto ciò che può essere dedotto dal contesto è abbreviabile come parametri
restituiti, tipi e parentisi.
Nel caso si scriva una sola riga di codice, petanto con le grafe omesse, per
restituire un valore va omesso il «return».
Esempio di espressione lambda ridotta al minimo:
e -> e.getEl(); //nessuna parentisi tonda perché restituisce un solo
paramentro e nessun tipo in quanto deducibile dal contesto, inoltre sono omesse
grafe e return in quanto il codice è costituito da una sola istruzione
3. Perché le Espressioni Lamba
Java nasce come paradigma ad oggetti e mai in passato ha voluto discostarsi da
questa sua caratteristica che lo rendeva ottimo in certe applicazioni ma lo
indebolisce con l’esplosione del multicore computing a cui la programmazione
funzionale si adatta meglio.
Nell’ottica di permettere la programmazione funzionale e di sintetizzare al
massimo il codice, Java 8 reinserisce le Espressioni Lambda.
4. Quando usare le Espressioni Lambda
Le Espressioni Lambda si usano per ridefinire il Metodo Astratto (SAM: Single Abstract
Metod) di un’interfaccia funzionale in base al contesto.
Ad esempio un filtro può dipendere dal contesto o dalla richiesta ed ecco che con le
espressioni lambda possiamo ridefinirlo di volta in volta in base alle nostre necessità.
Esempio Videoteca: (Nell’esempio vediamo un metodo chiamare il SAM dell’IF che
definiremo tramite Espressione Lambda nel al momento di utilizzarla nel main)
…
Videoteca videoteca = new Videoteca();
//Filtro per Valutazione:
Film[] beiFilm = videoteca. getFilmFiltrati (film->
film.getMediaRecensioni() > 3);
//Filtro per genere:
Film[] fantasy = videoteca. getFilmFiltrati (film->
‘’Fantasy’’.equals(film.getGenere()));
@FunctionalInterface
public Interface FiltroFilm {
boolean filtra (Film film);
}
public Class Videoteca {
public Film[] getFilmFiltrati (FiltroFilm
filtroFilm) {
…
if (filtroFilm.filtra(film))
filmFiltrati[i++]=film;
…
}
}
5. Esercizio
Creare un sistema Videoteca contenente un elenco di Film di cui son noti
Codice, Titolo, Autore, Genere, Durata e Media Recensioni.
Il sistema deve poter restituire:
L’elenco dei Film per autore;
L’elenco dei film per genere;
L’elenco dei film con media voto superiore alla soglia indicata;
L’elenco dei film per durata.
Assumere di essere in possesso di un database di film in un file csv in cui ogni
film è in una riga ed i campi sono separati da virgola.
6. Soluzione: La classe Films
package Videoteca;
public class Films {
private int codice;
private String titolo;
private String autore;
private String genere;
private int durata;
private double mediaRecensioni;
public Films(int codice, String
titolo, String autore, String genere, int
durata, double mediaRecensioni){
this.codice = codice;
this.titolo = titolo;
this.autore = autore;
this.genere = genere;
this.durata = durata;
this.mediaRecensioni =
mediaRecensioni;
}
public void setCodice(int codice){
this.codice = codice;
}
public void setTitolo(String titolo){
this.titolo = titolo;
}
public void setAutore(String autore){
this.autore = autore;
}
public void setGenere(String
genere){
this.genere = genere;
}
public void setDurata(int durata){
this.durata = durata;
}
public void
setMediaRecensioni(double
mediaRecensioni){
this.mediaRecensioni =
mediaRecensioni;
}
public int getCodice(){
return this.codice;
}
public String getTitolo(){
return this.titolo;
}
public String getAutore(){
return this.autore;
}
public String getGenere(){
return this.genere;
}
public int getDurata(){
return this.durata;
}
public double getMediaRecensioni(){
return this.mediaRecensioni;
}
}
8. Soluzione: La classe Videoteca
package Videoteca;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class Videoteca {
private HashMap<Integer, Films> films;
public Videoteca(Films[] listaFilm){
this.films = new HashMap<Integer, Films>();
for (Films film : listaFilm) {
this.films.put(film.getCodice(), film);
}
}
public Videoteca(List<Films> listaFilm){
this.films = new HashMap<Integer, Films>();
for (Films film : listaFilm) {
this.films.put(film.getCodice(), film);
}
}
@SuppressWarnings("unchecked")
public Videoteca(File listaFilm){
//letturaFile e creazione mappa di Films
//List<String> readAllLines(Path path)
ArrayList<Films> filmList = new ArrayList<Films>();
try {
String SEPARATOR = ",";
BufferedReader listaReader = new
BufferedReader(new FileReader(listaFilm));
filmList = (ArrayList<Films>)
listaReader.lines().map(line -> Arrays.asList(line.split(SEPARATOR)));
listaReader.close();
for (Films film : filmList) {
this.films.put(film.getCodice(), film);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
9. Soluzione: La classe Videoteca
public List<Films> cerca(FiltroFilm filtroFilm){
ArrayList<Films> filmList = new ArrayList<Films>(this.films.values());
ArrayList<Films> filmFiltrati = new ArrayList<Films>();
for (Films film : filmList) {
if (filtroFilm.filtra(film))
filmFiltrati.add(film);
}
return filmFiltrati;
}
}
10. Soluzione: Il main
package Videoteca;
import java.io.File;
import java.util.List;
import java.util.Scanner;
public class Mediateca {
public static void main(String[] args) {
File listafilm = new File(args[0]);
// TODO Auto-generated method stub
Videoteca videoteca = new Videoteca(listafilm);
//Film per autore;
String autore;
Scanner a = new Scanner(System.in);
autore = a.nextLine();
a.close();
List<Films> filmAutore = videoteca.cerca(film-
>autore.equals(film.getAutore()));
//Film per genere;
String genere;
Scanner g = new Scanner(System.in);
genere = g.nextLine();
g.close();
List<Films> filmGenere = videoteca.cerca(film-
>genere.equals(film.getGenere()));
//Film per media;
int soglia;
Scanner s = new Scanner(System.in);
soglia = Integer.valueOf(s.nextLine());
s.close();
List<Films> beiFilm = videoteca.cerca(film-
>film.getMediaRecensioni() > soglia);
//Film per durata minima;
int durata;
Scanner d = new Scanner(System.in);
durata = Integer.valueOf(d.nextLine());
d.close();
List<Films> filmLunghi = videoteca.cerca(film-
>film.getDurata() > durata);
}
}
11. Soluzione: Osservazioni
Nella soluzione offerta possiamo osservare come nel metodo «cerca()» della classe
Videoteca venga richiamato il metodo astratto dell’Interfaccia Funzionale FiltroFilm.
Come in ogni Interfaccia il Metodo non è implementato quindi ci è dato sapere cosa ci
verrà restituito (in questo caso un boolean) ma non come questo verrà elaborato.
Le espressioni Lambda che abbiamo usato nel nostro esempio vanno a ridefinire il
metodo di volta in volta, permettendoci di avere risultati differenti senza dover
richiamare o creare nuovi metodi.
Per effetto dell’ereditarietà molte volte l’Interfaccia Funzionale non è esplicitata,
caso evidente con l’uso delle collezioni e degli stream che implementano Interfacce
quali Iterable e Comparable.
Es:
List<String> strings = new ArrayList<>();
strings.stream().forEach((string) -> {
System.out.println("Content: " +
string);
});
Es2:
strings.parallelStream().
filter(s -> s.contains("java")).
forEach((string) -> {
System.out.println("Content: " +
string);
});
}