Académique Documents
Professionnel Documents
Culture Documents
Plan
Programmation fonctionnelle
Lambda expression
Interface fonctionnelle
API Stream
1
Principe programmation fonctionnelle
Considére une fonction comme une entité de première classe (first
class citizen) sur laquelle on peut appliquer les mêmes opérations
que n’importe quel autre élément du langage
2
Expression Lambda et Interfaces
fonctionnelles
Mastère de recherche M1- ENSI
F.Kboubi , ferihane.kboubi@ensi-uma.tn
3
Interface fonctionnelle
Elle ne doit avoir qu'une seule méthode déclarée abstraite
public interface InterfaceFonctionnel {
public void show();
}
Les méthodes définies dans la classe Object ne sont pas prises en compte
comme étant des méthodes abstraites
Elle peut avoir des méthodes par défaut, ainsi que des méthodes de classe
Toutes les méthodes doivent être publiques
public interface InterfaceFonctionnel {
public void show();
public default int addition(int a, int b) {
return a + b;
}
public static int soustraction(int a, int b) {
return a - b;
}
}
Interface fonctionnelle
L'annotation @FunctionalInterface permet d'indiquer explicitement
au compilateur que l'interface est une interface fonctionnelle
Le compilateur pourra alors vérifier que les contraintes d’une interface fonctionnelles sont
respectées.
Son utilisation est facultative.
4
Usage d’une interface fonctionnelle
Pour créer un objet qui implémente une interface fonctionnelle il existe trois moyens :
Écrire une nouvelle classe nommée qui implémente la méthode, et créer un l’objet à partir de cette classe
public Classe implements InterfaceFonctionnel{
public void show() {
System.out.println("Implémentation méthode")
}
}
Classe c=new Classe();
c.show();
java.util.function
Afin d’éviter aux développeurs de créer systématiquement leurs interfaces,
le package java.util.function déclare les interfaces fonctionnelles les plus
utiles.
Les interfaces fonctionnelles dont le nom se termine par:
Function déclarent une méthode qui prend un ou plusieurs paramètres et qui
retourne un résultat.
Operator déclarent une méthode qui prend un ou plusieurs paramètres du même
type et retourne un résultat du même type.
Consumer déclarent une méthode qui prend un ou plusieurs paramètres mais qui ne
retourne aucun résultat.
Supplier déclarent une méthode qui ne prend aucun paramètre mais qui retourne un
résultat.
Predicate déclarent une méthode qui prend un paramètre et qui retourne un
résultat de type booléen.
5
java.util.function
Expression Lambda
Une fonction anonyme sans déclaration explicite du type de retour, ni de
modificateurs d'accès, ni de nom.
Elle est utile lorsque le traitement n'est utilisé qu'une seule fois
6
Expression Lambda
Lorsque l'expression lambda est évaluée par le compilateur, celui-ci
infère le type vers l'interface fonctionnelle.
Cela lui permet d'obtenir des informations sur les paramètres utilisés,
le type de la valeur de retour, les exceptions qui peuvent être levées.
Elle ne réduit pas l'aspect orienté objet du langage mais rend celui-ci
plus riche pour certaines fonctionnalités.
7
La syntaxe des expressions lambda
Type facultative : Pas besoin de déclarer le type d’un paramètre. Le
compilateur peut déduire le type de la valeur du paramètre.
8
Exemple avec interface fonctionnelle
public class Main {
public static void main(String args[]) {
//avec la déclaration de type
Operation addition = (int x, int y) -> x + y;
interface Operation {
int calc(int x, int y);
}
9
Avec classes anonymes
public class Client {
public static void main(String args[]) {
}
}
10
Avec Expression Lambda
public class Client {
public static void main(String args[]) {
// Afficher les documents qui contiennent le mot "du" avec une fréquence = 2
System.out.println("Lambda Expression 3");
c.printDocument(d->{if(d.getWordFreq().containsKey("du"))
{return d.getWordFreq().get("du")==2;}
else return false;});
// Afficher les documents qui ne contiennent pas les mots <deuxième, un>
System.out.println("Lambda Expression 4");
Set<String> s=new HashSet<String>();s.add("deuxième");s.add("un");
c.printDocument(d->!d.getVocab().containsAll(s));
}
}
Le corps d’une lambda peut accéder au contenu d’une variable déclarée dans la closure.
Mais ne peut pas modifier le contenu d’un paramètre ou d’une variable issue d’une closure
int i = 0;
liste.forEach(e -> i += e); // ERREUR DE COMPILATION : la variable i ne peut pas
// être modifiée car elle fait partie de la closure.
11
L’opérateur :: de référence de méthode
L’opérateur :: permet de référencer une méthode en évitant de déclarer une
lambda.
Rend le code plus lisible
La classe Optional
Dans beaucoup de langages de programmation, nous avons la possibilité d’affecter à
des variables, des paramètres ou des attributs une référence nulle.
Cela peut conduire à toutes sortes de bugs (NullPointerException en Java)
Optional permet de représenter la possibilité qu’il existe ou non une valeur sans avoir
besoin d’utiliser null
Optional<String> v = Optional.ofNullable("test");
if (v.isPresent()) {
System.out.println(v);
} else {
System.out.println("valeur non trouvée");
}
Optional<String> v = Optional.ofNullable("test");
//retourne la valeur de l'optional si elle est définie ou la valeur par défaut passée en paramètre.
System.out.println(v.orElse("valeur non trouvée"));
//invoque la méthode passée en référence uniquement si une valeur est définie par l'optional
v.ifPresent(System.out::println);
12
LAMBDA EXPRESSIONS ET API STREAM
API Streams
L’API streams a été introduite avec Java 8 pour permettre la
programmation fonctionnelle.
Un stream (flux) est une représentation d’une séquence sur laquelle
il est possible d’appliquer des opérations.
Elle permet d’effectuer les opérations sur une séquence sans utiliser
de structure de boucle (bonne lisibilité du code)
Les opérations sur les streams sont réalisées en flux (d’où leur nom)
ce qui limite l’empreinte mémoire nécessaire.
Il est possible de réaliser des traitements en parallèle pour tirer partie
des possibilités d’un processeur multi-cœurs ou d’une machine
multi-processeurs.
13
Création d’un stream
On peut créer un Stream en utilisant un objet de type builder
Stream<String> stream = Stream.<String>builder().add("Hello").add("World").build();
Méthode filter()
Méthode filter() permet d’appliquer un filtre pour éliminer
une partie de ses éléments
// On affiche les 500 premiers nombres qui ne sont pas divisibles par 7
IntStream.iterate(1, n -> n + 1)
.filter(n -> n % 7 != 0)
.limit(500)
.forEach(System.out::println);
// On affiche les 500 premiers nombres qui ne sont pas divisibles par 7
// et qui sont impairs
IntStream.iterate(1, n -> n + 1)
.filter(n -> n % 7 != 0)
.filter(n -> n % 2 != 0)
.limit(500)
.forEach(System.out::println);
14
Méthode map()
Méthode map() permet de transformer la nature du stream afin de
passer d’un type à un autre
List<Voiture> liste = Arrays.asList(new Voiture("citroen"), new Voiture("audi"),
new Voiture("renault"), new Voiture("volkswagen"),
new Voiture("citroen"));
Méthode reduce()
Permet d’appliquer une action pour réduire le stream et obtenir un résultat
unique
System.out.println(plusLongue.orElse("Liste vide"));
15
Méthode collect()
Permet de créer une nouvelle collection à partir d’un stream
List<String> liste = Arrays.asList("une chaine", "une autre chaine", "encore une chaine");
List<String> autreListe = liste.stream().collect(Collectors.toList());
IntStream.range(1, 100).map(v->2*v).limit(10).forEach(System.out::println);
IntStream.range(1, 100).map(v->2*v).skip(10).forEach(System.out::println);
16
Méthode allMatch(), anyMatch(),noneMatch()
allMatch(), s’assure que tous les éléments du stream vérifient la
condition
List<String> names=Arrays.asList("John","Joe","Jack","Jane");
Boolean check=names.stream().allMatch(v->v.startsWith("J"));
System.out.println(check);
Méthode forEach()
Permet d’appliquer un traitement sur chaque élément du stream
Programmation impérative
for(String e : collection) {
System.out.println(e);
}
17
Méthode parallel()
Un stream en parallèle découpe le flux pour assigner
l’exécution à différents processeurs et recombine ensuite
le résultat à la fin.
Cela signifie que les traitements sur le stream ne doivent
pas être dépendant de l’ordre d’exécution.
IntStream.range(1, 10).parallel().map(v->2*v).forEach(System.out::println);
Exercice
18
37 M1 - ENSI - TPA - F.KBOUBI
19