CSC4102 : Programmation orient´e objet en
JAVA — Lambda expressions, Streams, Optional
Denis Conan
Janvier 2022
Sommaire
1. Motivations et objectifs 2. Lambda expressions JAVA 3. Utilisation dans les tests JUnit
4. Utilisation dans la manipulation des collections : les Streams 5. Utilisation dans la gestion des r´ ef´ erences null : Optional 6. Streams + Optional
7. Mise en pratique en TP (2h) + HP (3h)
2/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
1 Motivations et objectifs
1.1 Qui demande de la programmation fonctionnelle ? 1.2 Pourquoi des fonctions ?
1.3 Quels sont les objectifs de la s´ eance ?
3/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
1.1 Qui demande de la programmation fonction- nelle ?
Brian Goetz, Java Language Architect, Oracle
Specification lead for JSR-335 (Lambda Expressions for the Java Programming Language) Foreword of [Naftalin, 2015]
■ H´ eritage et type param´ etr´ e = abstractions pour les donn´ ees et les m´ ethodes
■ Lambda expression = abstraction pour les fonctions
• 2014 : ajout des lambda expressions dans la version 8 de JAVA
• 2009 : lancement du projet Lambda
• 2006 : propositions pour ajouter le concept de « clˆ oture »/« fermeture » (closure)
• 1997 : ajout du concept de « classe anonyme » (anonymous inner class)
• 1941 : travaux d’Alonzo Church sur la th´ eorie du calcul, d’o` u vient la terminologie
■ Ce sont les premiers pas vers la programmation orient´ ee fonction, appel´ ee aussi programmation fonctionnelle
4/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
1.2 Pourquoi des fonctions ?
■ Exemple 1 : pour programmer des r´ eactions
`
a des ´ ev´ enements d’IHM
■ Exemple 2 : pour parall` eliser des actions sur des grandes collections de donn´ ees
5/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
myComboBox.setOnAction((event)->{...traitements });
double maxDistance = pointList.parallelStream()
.map(p->p.distance(0, 0)) .reduce(Double::max) .orElse(0.0);
fork
fork fork
map map map map
join
reduce reduce
join join
reduce
fonctions
1.3 Quels sont les objectifs de la s´ eance ?
■ Concept de lambda expression en JAVA
■ (Utilisation dans les tests JUnit)
■ Utilisation dans la manipulation des collections avec les Streams JAVA
■ Utilisation dans la gestion des r´ ef´ erences null : Optional
■ Mais nous n’´ etudions pas la programmation orient´ ee fonction ` a proprement parler dans CSC4102
• Pour d´ ecouvrir ce sujet, p.ex. :
P.-Y. Saumont, Functional Programming in Java—How functional techniques improve your Java programs,
[Saumont, 2017]6/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
2 Lambda expressions JAVA
2.1 Pr´ eliminaire : interface fonctionnelle 2.2 D´ efinition et syntaxe
2.3 Exemples de lambda expressions
2.4 Contexte d’ex´ ecution d’une lambda expression 2.5 R´ ef´ erence de m´ ethode*
2.6 Interfaces fonctionnelles standard*
. Les ´ el´ ements de cette section sont extraits des r´ ef´ erences
[Naftalin, M., 2012],
[Naftalin, 2015], et
[Bloch, 2018]7/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
2.1 Pr´ eliminaire : interface fonctionnelle
■ Une interface fonctionnelle
• Est une interface
• Poss` ede une seule m´ ethode abstraite
• Est annot´ ee @FunctionalInterface
■ Un exemple : public interface Consumer<T>
{void accept(T t);
}■ Objectif : simplifier le code avec du sucre syntaxique
• De
pointList.forEach(
new Consumer<Point>(){public void accept(Point p){p.translate(1,1);}}
• ); A `
pointList.forEach(p -> p.translate(1, 1));
8/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
2.2 D´ efinition et syntaxe
■ Lambda expression = conceptuellement une fonction
• Arguments en entr´ ee et valeur de retour
■ Syntaxe
• Soit : (parameters) -> expression
• Soit : (parameters) -> { statements; }
■ C’est un objet = une instance d’une interface (fonctionnelle
1)
Runnable r = () ->{}; // cr´ee unelambda expression
// et affecte une r´ef´erence vers cette lambda expression `a r Object o = r; // transtypage vers le haut, comme pour une r´ef´erence/un objet
■ Une lambda peut ˆ etre utilis´ ee l` a o` u une interface fonctionnelle est d´ eclar´ ee
• Interface fonctionnelle = ⇒ une m´ ethode = ⇒ pas d’ambigu¨ıt´ e
• P.ex., « public void forEach(Consumer<? super T> consumer); » permet d’´ ecrire « pointList.forEach(p -> p.translate(1, 1)); » 1. Interface fonctionnelle = interface avec une unique m´ ethode abstraite
9/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
2.3 Exemples de lambda expressions
■ Types des arguments explicites (1, 4) ou inf´ er´ es (2, 5, 6)
• Pas de m´ elange entre « explicite » et « inf´ er´ e »
■ Le corps peut ˆ etre un bloc (6) ou une expression (1–5)
• Bloc retournant une valeur (dit value-compatible) ou rien (dit void-compatible)
• Idem pour l’expression : une valeur (1, 2, 3, 5) ou rien (4)
■ Un seul argument = ⇒ possible d’omettre les parenth` eses autour de l’argument
10/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
1. (int x, int y) -> x + y
// retourne la somme des deux arguments2. (x, y) -> x - y
// retourne la diff´erence3. () -> 42
// pas d’argument, retourne424. (String s) -> System.out.println(s)
// affiche l’argument et ne retourne rien5. x -> 2 * x
// un argument (sans parenth`ese)6. c ->
{int s = c.size(); c.clear(); return s;
} // type de l’argument poss´edant les m´ethodes size()etclear(), p.ex. une collection2.4 Contexte d’ex´ ecution d’une lambda expres- sion
■ Une lambda expression s’ex´ ecute dans un contexte
• Comme attribut de classe : class Bar { Foo foo = () -> 42; }
• Comme variable locale : void bar() { Foo foo = () -> 42; } ;
■ Elle peut utiliser les variables de son contexte, y compris « this » lorsqu’elle est imbriqu´ ee dans une instance de classe
■ R` egles classiques d’utilisation et de nommage des ´ el´ ements du contexte
• Un exemple l´ egal : l’argument « i » de la lambda cache l’attribut « i »
− class Bar { int i; Foo foo = i -> i * 2; } ;
• Un exemple ill´ egal : « i » est d´ ej` a une variable locale de « bar »
− void bar() { int i; Foo foo = i -> i * 2; } ;
11/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
2.5 R´ ef´ erence de m´ ethode*
■ Cas particulier d’une lambda expression avec un seul argument
+ l’expression est un unique appel ` a une m´ ethode avec un seul argument
• Par exemple : str -> Integer.parseInt(str)
• Simplification de l’´ ecriture avec la forme « r´ ef´ erence de m´ ethode »
− Pour le mˆ eme exemple : Integer::parseInt
■ Plus g´ en´ eralement, une lambda expression peut ˆ etre repr´ esent´ ee par une m´ ethode concr` ete d’une classe
= ⇒ Une r´ ef´ erence de m´ ethode est un raccourci d’´ ecriture d’une lambda expression avec un argument et l’expression form´ ee de l’appel unique de la m´ ethode r´ ef´ erenc´ ee
■ Syntaxe (plus d’informations dans le document « pour aller plus loin »)
• ReferenceType::identifier pour m´ ethode de classe, p.ex. « Integer::parseInt »
• ObjectReference::identifier pour m´ ethode d’instance : p.ex. « System.out::println »
• ReferenceType::new pour constructeur, p.ex. « ArrayList::new »
12/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
2.6 Interfaces fonctionnelles standard*
■ java.util.function
[PackageJavaUtilFunction, JSE8]• Plus de 40 fonctions
− Dont 6 de base : Operator, Predicate, Function, Supplier, Consumer
− Avec des variantes pour les types primitifs
Interface Signature
la fonction
Exemple
1 UnaryOperator<T> T apply(T t) String::toLowerCase 2 BinaryOperator<T> T apply(T t1, T, t2) BigInteger::add 3 Predicate<T> boolean test(T t) Collection::isEmpty 4 Function<T, R> R apply(T t) Arrays::asList 5 BiFunction<T, U, R> R apply(T t, U u) (x, y) -> x - y
6 Supplier<T> T get() Instant::now
7 Consumer<T> void accept(T t) System.out::println 8 ToIntFunction<T> int applyAsInt(T t) Integer::parseInt 9 IntFunction<R> R apply(int i) int[]::new
■ Pour chaque ligne du tableau, on peut ´ ecrire
« colonne Interface » ref = « colonne Exemple »
• P.ex. Function<ArrayList, List> obj = Arrays::asList;
13/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
2.6.1 Exemples d’utilisation (de base)
Classeseance8/InterfaceFonctionnelleStandard
1 // i n t e r f a c e s f o n c t i o n n e l l e s de b a s e
2 U n a r y O p e r a t o r < String > f1 = S t r i n g :: t o L o w e r C a s e ; 3 S y s t e m . out . p r i n t l n ( f1 . a p p l y ( " ABC " ) ) ; //
4 B i n a r y O p e r a t o r < B i g I n t e g e r > f2 = B i g I n t e g e r :: add ;
5 B i g I n t e g e r a = n e w B i g I n t e g e r ( " 2 " ) , b = n e w B i g I n t e g e r ( " 3 " ) ; 6 S y s t e m . out . p r i n t l n ( f2 . a p p l y ( a , b ) ) ; //
7 P r e d i c a t e < C o l l e c t i o n < String > > f3 = C o l l e c t i o n < String >:: i s E m p t y ; 8 S y s t e m . out . p r i n t l n ( f3 . t e s t (n e w A r r a y L i s t < String >() ) ) ; //
9 F u n c t i o n < S t r i n g [] , List < String > > f4 = A r r a y s :: a s L i s t ; 10 S y s t e m . out . p r i n t l n ( f4 . a p p l y (n e w S t r i n g [] { " a " , " b " }) ) ; //
11 S u p p l i e r < Instant > f5 = I n s t a n t :: now ; 12 S y s t e m . out . p r i n t l n ( f5 . get () ) ; //
13 C o n s u m e r < String > f6 = S y s t e m . out :: p r i n t l n ; 14 // v a r i a n t e p o u r les t y p e s p r i m i t i f s
15 T o I n t F u n c t i o n < String > f7 = I n t e g e r :: p a r s e I n t ; 16 I n t F u n c t i o n <i n t[] > f8 = i n t[ ] : :n e w;
17 f6 . a c c e p t ( " abc " ) ; S y s t e m . out . p r i n t l n ( f7 . a p p l y A s I n t ( " 11 " ) ) ; //
18 i n t[] t = f8 . a p p l y (4) ; S y s t e m . out . p r i n t l n ( t . l e n g t h ) ; //
Affichage :
«abc», puis «5», «true», «[a, b]», «2019-03-13T21:06:26.529Z», «abc», «11», et enfin «4»
14/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
3 Utilisation dans les tests JUnit
■ Le second argument de la m´ ethode de classe Assert.assertThrows est une expression lambda
• Expression de type ThrowingRunnable, qui est une interface fonctionnelle
• L’expression n’a pas d’argument en entr´ ee : « () »
• L’expression ex´ ecute une m´ ethode qui ne retourne aucune valeur : « void »
• Lev´ ee d’une exception AssertionError si l’expression lambda ne l` eve pas d’exception
Classeseance6.mediathequesimplifie.testdevalidation.TestAjouterLocalisation 1 @ T e s t
2 p u b l i c v o i d t e s t A j o u t e r L o c a t i o n T e s t 4 P u i s 3 () t h r o w s O p e r a t i o n I m p o s s i b l e { 3 m e d i a t h e q u e . a j o u t e r L o c a l i s a t i o n ( " s1 " , " r1 " ) ;
4 A s s e r t . a s s e r t T h r o w s ( O p e r a t i o n I m p o s s i b l e .class,
5 () - > m e d i a t h e q u e . a j o u t e r L o c a l i s a t i o n ( " s1 " , " r1 " ) ) ; 6 }
• OperationImpossible
assertThrows(Class<OperationImpossible>, ThrowingRunnable)
− Avec
public interface ThrowingRunnable
{void run() throws Throwable;
}. Depuis JUnit version 4.13.
15/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
4 Utilisation dans la manipulation des collections : les Streams
4.1 Retour sur les motivations et les objectifs des lambda expressions 4.2 Pipeline, stream, et ´ evaluation tardive
4.3 Quelques exemples
. Les ´ el´ ements de cette section sont extraits des r´ ef´ erences
[Naftalin, M., 2012],
[Naftalin, 2015], et
[Bloch, 2018]16/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
4.1 Retour sur les motivations et les objectifs des lambda expressions
■ Motivation 1 : introduire des ´ elements de programmation orient´ ee fonction
■ Motivation 2 : raccourcir, les nouveaux sucres syntaxiques « -> » et « :: »
■ Motivation 3 : permettre des ex´ ecutions parall` eles
• C’est cette motivation qui donne la biblioth` eque des Streams
Imaginons une collection de grande taille ET un ordinateur avec bcp de cœurs
− Mise en œuvre du paradigme de programmation « Map/Reduce »
17/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
double maxDistance =
pointList.parallelStream()
.map(p -> p.distance(0, 0)) .reduce(Double::max) .orElse(0.0);
fork
fork fork
map map map map
join
reduce reduce
join join
reduce
4.2 Pipeline, stream, et ´ evaluation tardive
■ Philosophie UNIX : « At its heart is the idea that the power of a system comes more from the relationships among programs than from the programs themselves.
Many UNIX programs do quite trivial things in isolation, but, combined with other programs, become general and useful tools. »
[Kernighan and Pike, 1984]• Les lambda expressions = traitements ` a granularit´ e fine et composables
■ Pipeline = « d´ ebut » + « traitement interm´ ediaire » + « terminaison »
• Description d´ etaill´ ee dans le document « pour aller plus loin »
■ Stream = S´ equence de valeurs, ordonn´ ees ou non, et non stock´ ees
■ Pipeline de streams + ´ evaluation tardive
• C’est l’op´ eration terminale qui « tire » les valeurs
− Aucune valeur n’est calcul´ ee tant qu’elle n’est pas demand´ ee
IntStream.iterate(1, i -> i * 2) // g´ en´ eration
streamde taille infinie .limit(10) // limitation du nombre de g´ en´ erations
.forEach(System.out::println) // op´ eration qui « tire » les valeurs
18/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
4.3 Quelques exemples I
■ D´ ebut : g´ en´ eration d’une s´ equence infinie avec IntStream.iterate
Interm´ ediaire : troncature avec limit + insertion d´ everminage avec peek
Terminaison : test de pr´ esence avec anyMatch
■ D´ emonstration de l’´ evaluation tardive
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 b o o l e a n t r o u v e = I n t S t r e a m . i t e r a t e (1 , i - > i * 2)
2 . l i m i t ( 1 0 )
3 . p e e k ( S y s t e m . out :: p r i n t l n )
4 . a n y M a t c h ( v - > v == 16) ;
Affichage :
1 2 4 8 16
19/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
4.3 Quelques exemples II
■ D´ ebut : stream
deCollection
Interm´ ediaire : filtrage avec filter
Terminaison : recherche d’un ´ el´ ement avec findFirst
■ Recherche dans une collection avec filtrage + obtention de la premi` ere valeur trouv´ ee
• Hypoth` ese : la pr´ econdition du cas d’utilisation « ajouter un genre » inclut la condition « genre avec ce nom inexistant »
Classeetudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque
1 /* *
2 * c h e r c h e r G e n r e c h e r c h e un G e n r e d a n s la l i s t e des g e n r e s .
3 *
4 * @ p a r a m n o m G e n r e du G e n r e a c h e r c h e r
5 * @ r e t u r n le g e n r e c o r r e s p o n d a n t au nom d a n s la c o l l e c t i o n
6 */
7 p r i v a t e O p t i o n a l < Genre > c h e r c h e r G e n r e (f i n a l S t r i n g n o m G e n r e ) { 8 r e t u r n l e s G e n r e s . s t r e a m () . f i l t e r ( g - > g . g e t N o m () . e q u a l s ( n o m G e n r e ) ) .
f i n d F i r s t () ; 9 }
La classe Optional est ´ etudi´ ee dans la section qui suit celle-ci.
20/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
4.3 Quelques exemples III
■ D´ ebut : stream
deCollection
Interm´ ediaire : filtrage avec filter
Terminaison : test de pr´ esence avec anyMatch
■ Existe-t-il un document avec le genre donn´ e ?
Classeetudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque
1 /* *
2 * c h e r c h e un d o c u m e n t d o n t le g e n r e est i n d i q u e en p a r a m e t r e .
3 *
4 * @ p a r a m g G e n r e du d o c u m e n t a c h e r c h e r 5 * @ r e t u r n t r u e s ’ il en e x i s t e un f a l s e s i n o n
6 */
7 p r i v a t e b o o l e a n e x i s t e D o c u m e n t (f i n a l G e n r e g ) {
8 r e t u r n l e s D o c u m e n t s . v a l u e s () . s t r e a m () . a n y M a t c h ( d - > d . g e t G e n r e () . e q u a l s ( g ) ) ; 9 }
21/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
4.3 Quelques exemples IV
■ D´ ebut : g´ en´ eration d’une s´ equence infinie avec IntStream.iterate
Interm´ ediaire : troncature avec limit + transformation avec mapToObj
Terminaison : r´ eduction avec collect en une liste avec toList
deCollectors
■ Collecte dans une liste
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 List < String > l = I n t S t r e a m . i t e r a t e (1 , i - > i * 2)
2 . l i m i t ( 1 0 )
3 . m a p T o O b j ( S t r i n g :: v a l u e O f )
4 . c o l l e c t ( C o l l e c t o r s . t o L i s t () ) ;
5 S y s t e m . out . p r i n t l n ( l ) ;
Affichage :
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
■ Cet exemple d´ emontre implicitement une construction d’objet
• On pourrait ´ ecrire mapToObj(Integer::new) pour r´ ecup´ erer une List<Integer>
22/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
4.3 Quelques exemples V
■ D´ ebut : g´ en´ eration d’une s´ equence infinie avec IntStream.iterate
Interm´ ediaire : troncature avec limit + transformation avec mapToObj
Terminaison : r´ eduction avec collect en une chaˆıne de caract` eres avec
joining
deCollectors
■ Collecte dans une chaˆıne de caract` eres
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 S t r i n g s = I n t S t r e a m . i t e r a t e (1 , i - > i * 2)
2 . l i m i t ( 1 0 )
3 . m a p T o O b j ( S t r i n g :: v a l u e O f )
4 . c o l l e c t ( C o l l e c t o r s . j o i n i n g ( " ␣ + ␣ " ) ) ; 5 S y s t e m . out . p r i n t l n ( s ) ;
Affichage :
1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512
23/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
4.3 Quelques exemples VI
■ D´ ebut : g´ en´ eration d’un stream
Terminaison : application d’un effet de bord avec forEach
■ Exemple d’application d’un effet de bord
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 List < Point > p o i n t s = A r r a y s . a s L i s t (n e w P o i n t (1 , 2) , n e w P o i n t (2 , 4) ) ; 2 S y s t e m . out . p r i n t l n ( p o i n t s ) ;
3 p o i n t s . s t r e a m () . f o r E a c h ( p - > p . t r a n s l a t e (1 , 2) ) ; 4 S y s t e m . out . p r i n t l n ( p o i n t s ) ;
Affichage :
[java.awt.Point[x=1,y=2], java.awt.Point[x=2,y=4]]
[java.awt.Point[x=2,y=4], java.awt.Point[x=3,y=6]]
24/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
4.3 Quelques exemples VII
■ D´ ebut : g´ en´ eration de deux streams
Interm´ ediaire : concat´ enation des deux streams avec concat
deStream
+ retrait des doublons avec distinct
Terminaison : collecte dans un ensemble avec toSet
■ D´ emonstration de la concat´ enation de streams
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 List < List < String > > l i s t e D e L i s t e s = n e w A r r a y L i s t < >() ; 2 l i s t e D e L i s t e s . add ( A r r a y s . a s L i s t ( " a " , " b " , " c " ) ) ; 3 l i s t e D e L i s t e s . add ( A r r a y s . a s L i s t ( " c " , " d " , " a " ) ) ;
4 Set < String > c o n c a t = S t r e a m . c o n c a t ( l i s t e D e L i s t e s . get (0) . s t r e a m () ,
5 l i s t e D e L i s t e s . get (1) . s t r e a m () )
6 . d i s t i n c t ()
7 . c o l l e c t ( C o l l e c t o r s . t o S e t () ) ;
8 S y s t e m . out . p r i n t l n ( c o n c a t ) ;
Affichage :
[a, b, c, d]
Le prototype de la m´ethodeasListdeArraysest «List<String> asList(String... a)». Pour rappel, l’´ecriture «String...» est une commodit´e d’´ecriture est appell´eevarargs.
25/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
4.3 Quelques exemples VIII
■ Mˆ eme exemple avec g´ en´ eralisation ` a un nombre quelconque de listes
• D´ ebut : obtention de la s´ equence de listes de la liste englobante
Interm´ ediaire : transformation de chaque liste de String en un stream avec stream
deList et insertion dans une s´ equence de String
− flatMap = flattened map
• Retourne un Stream<String> au lieu d’un Stream<Stream<String>>
Classeseance8/lambdaexpressions/ExampleNaftalin2015
1 List < List < String > > l i s t e D e L i s t e s = n e w A r r a y L i s t < >() ; 2 l i s t e D e L i s t e s . add ( A r r a y s . a s L i s t ( " a " , " b " , " c " ) ) ; 3 l i s t e D e L i s t e s . add ( A r r a y s . a s L i s t ( " c " , " d " , " a " ) ) ; 4 Set < String > f l a t M a p = l i s t e D e L i s t e s . s t r e a m ()
5 . f l a t M a p ( L i s t :: s t r e a m )
6 . d i s t i n c t ()
7 . c o l l e c t ( C o l l e c t o r s . t o S e t () ) ;
8 S y s t e m . out . p r i n t l n ( f l a t M a p ) ;
26/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
5 Utilisation dans la gestion des r´ ef´ erences null : Optional
5.1 Probl` eme des r´ ef´ erences null
5.2 Solution non satisfaisante : documentation 5.3 Solution/idiome JAVA : classe Optional
27/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
5.1 Probl` eme des r´ ef´ erences null
■ Situations dans lesquelles « pointeur null » signifie « absence de valeur »
• P.ex. valeur de retour pouvant ˆ etre null
Classeseance8/lambdaoptional/DocumentSansOptional
1 p u b l i c S t r i n g g e t N o m S o u s G e n r e () { 2 S t r i n g r e s u l t = " i n c o n n u " ;
3 if ( g e n r e != n u l l) { // a t t r i b u t p o u v a n t e t r e n u l l 4 S o u s G e n r e sg = g e n r e . g e t S o u s G e n r e () ;
5 if ( sg != n u l l) { // v a r i a b l e p o u v a n t e t r e n u l l 6 S t r i n g n = sg . g e t N o m () ;
7 if ( n != n u l l) { // i n u t i l e si l ’ i n v a r i a n t le v e r i f i e
8 r e s u l t = n ;
9 }
10 }
11 }
12 r e t u r n r e s u l t ; 13 }
28/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
SousGenre
Genre nom:string nbEmprunts:integer=0
Document code:string titre:string auteur:string
correspond
0..1
nom:string nbEmprunts:integer=0
0..1
possède
*
getSousGenre()
5.2 Solution non satisfaisante : documentation
■ Besoin d’avertir le programmeur de la m´ ethode appelante
• Premier essai : utiliser la documentation Javadoc
Classeseance8/lambdaoptional/GenreSansOptional
1 /* *
2 * o b t i e n t le sous - g e n r e .
3 *
4 * @ r e t u r n le sous - genre , qui p e u t e t r e { @ c o d e n u l l }.
5 */
6 p u b l i c S o u s G e n r e g e t S o u s G e n r e () { 7 r e t u r n s o u s G e n r e ;
8 }
29/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
5.3 Solution/idiome JAVA : classe Optional
■ La solution/l’idiome JAVA :
• Similairement ` a d’autres langages comme Groovy ou Scala,
JAVA propose depuis la version 8 un idiome pour rendre explicite la possibilit´ e qu’une r´ ef´ erence puisse ˆ etre null
et ainsi alerter le programmeur pour qu’il ´ evite le d´ er´ ef´ erencement dans ce cas
− P.ex. : Optional<SousGenre> getSousGenre()
• Pour votre culture g´ en´ erale, conf´ erence de C.A.R. Hoare, qui en 1965 a introduit le concept de « r´ ef´ erence null », ce qu’il consid` ere maintenant comme une erreur :
− Conf´ erence en 2009 : « Null References: The Billion Dollar Mistake »
30/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
5.3.1 Classe Optional
■ Classe conteneur et param´ etr´ ee par le type du contenu : Optional<T>
■ Pas de constructeur, mais cr´ eation par m´ ethodes de classes dites
« fabriques » (factories)
• Optional<Genre> g = Optional.of(genre) :
valeur null non autoris´ ee (exception NullPointerException )
• Optional<Genre> g = Optional.ofNullable(genre) : valeur null autoris´ ee
• Optional<Genre> g = Optional.empty() : contient null
■ Des m´ ethodes pour r´ ecup´ erer la valeur ou tester si null
•
g.isPresent(): retourne false si contenu null
• g.get()
: l`eve l’exceptionNoSuchElementException
si contenunull
•
g.orElse("inconnu"): retourne contenu non null , sinon "inconnu"
■ Des m´ ethodes pour manipuler la valeur si non null
•
sg.map(SousGenre::getNom): applique la lambda expression si non null et retourne la valeur dans un Optional (p.ex. ici, un Optional<String> )
•
sg.flatMap(SousGenre::getNom): idem map , mais sans mettre le r´ esultat dans un Optional (p.ex. ici retourne un String )
31/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
5.3.2 O` u mettre Optional ?
■ « Optional is primarily intended for use as a method return type where there is a clear need to represent “no result,” and where using null is likely to cause errors. »
[Class Optional, JSE8]■ « The intention of the Optional class is not to replace every single null reference »
[Urma, 2014]■ Dans le module CSC4102, Optional en guise de valeur de retour lorsque null est fonctionnellement possible (pas un cas d’erreur)
Classeseance8/lambdaoptional/GenreAvecOptional
1 /* *
2 * o b t i e n t le sous - g e n r e .
3 *
4 * Le d i a g r a m m e de c l a s s e s s p e c i f i e qu ’ un sous - g e n r e est o p t i o n n e l .
5 *
6 * @ r e t u r n le sous - genre , qui p e u t e t r e { @ c o d e n u l l }.
7 */
8 p u b l i c O p t i o n a l < S o u s G e n r e > g e t S o u s G e n r e () { 9 r e t u r n O p t i o n a l . o f N u l l a b l e ( s o u s G e n r e ) ; 10 }
32/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
5.3.3 Manipulation d’un objet Optional (1/2)
■ Mˆ eme exemple qu’au d´ ebut de la section avec imbrication de if
• isPresent : retourne true si un objet est pr´ esent
• get : retourne la valeur pr´ esente, sinon l` eve l’exception NoSuchElementException
Classeseance8/lambdaoptional/DocumentAvecOptional1
1 p u b l i c S t r i n g g e t N o m S o u s G e n r e () { 2 S t r i n g r e s u l t = " i n c o n n u " ; 3 if ( g e t G e n r e () . i s P r e s e n t () ) {
4 O p t i o n a l < S o u s G e n r e > sg = g e n r e . g e t S o u s G e n r e () ; 5 if ( sg . i s P r e s e n t () ) {
6 S t r i n g n = sg . get () . g e t N o m () ;
7 if ( n != n u l l) { // i n u t i l e si l ’ i n v a r i a n t le v e r i f i e
8 r e s u l t = n ;
9 }
10 }
11 }
12 r e t u r n r e s u l t ; 13 }
33/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
5.3.4 Manipulation d’un objet de Optional (2/2)
■ Meilleur code
Classeseance8/lambdaoptional/DocumentAvecOptional2 1 p u b l i c S t r i n g g e t N o m S o u s G e n r e () { 2 r e t u r n g e t G e n r e ()
3 . f l a t M a p ( G e n r e A v e c O p t i o n a l :: g e t S o u s G e n r e ) 4 . map ( S o u s G e n r e :: g e t N o m )
5 . o r E l s e ( " i n c o n n u " ) ; 6 }
• Utilisation de flatMap, map, et orElse
− GenreAvecOptional::getSousGenre retourne un Optional<SousGenre>
− D’o` u,
getGenre().map(GenreAvecOptional::getSousGenre)retournerait un objet de type
Optional<Optional<SousGenre>>− Donc, flatMap pour obtenir un objet de type Optional<SousGenre> au lieu d’un objet de type Optional<Optional<SousGenre>>
flatMap : This method is similar to map(Function), but the mapping function is one whose result is already an Optional, and if invoked, flatMap does not wrap it within an additional Optional
[Class Optional, JSE8]
− orElse : retourne la valeur si pr´ esente (≡ get ), sinon une valeur par d´ efaut
34/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
6 Streams + Optional
■ R` egle 45 de « Effective JAVA »
[Bloch, 2018]: « Use streams judiciously »
• « Overusing streams makes programs hard to read and maintain »
Classeetudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque
1 p r i v a t e O p t i o n a l < F i c h e E m p r u n t > c h e r c h e r E m p r u n t (f i n a l S t r i n g nom , f i n a l S t r i n g prenom , f i n a l S t r i n g c o d e ) t h r o w s O p e r a t i o n I m p o s s i b l e {
2 r e t u r n l e s E m p r u n t s
3 . s t r e a m ()
4 . f i l t e r ( f - > f . c o r r e s p o n d (
5 l e s C l i e n t s . v a l u e s () . s t r e a m ()
6 . f i l t e r ( c - > c . g e t N o m () . e q u a l s ( nom ) && c . g e t P r e n o m () . e q u a l s ( p r e n o m ) )
7 . f i n d F i r s t ()
8 . o r E l s e T h r o w (() - > n e w I l l e g a l A r g u m e n t E x c e p t i o n ( 9 " pas ␣ de ␣ c l i e n t ␣ ’ " + nom + " ␣ " + p r e n o m + " ’ " ) ) ,
10 l e s D o c u m e n t s . v a l u e s () . s t r e a m ()
11 . f i l t e r ( d - > d . g e t C o d e () . e q u a l s ( c o d e ) )
12 . f i n d F i r s t ()
13 . o r E l s e T h r o w (() - > n e w I l l e g a l A r g u m e n t E x c e p t i o n (
14 " pas ␣ de ␣ d o c u m e n t ␣ ’ " + c o d e + " ’ " ) ) ) )
15 . f i n d F i r s t () ;
16 }
35/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
7 Mise en pratique en TP (2h) + HP (3h)
■ Utilisation des Streams et de Optional dans certaines parties du code
■ Continuation du d´ eveloppement de l’application de l’´ etude de cas
■ Rendu de la s´ eance en HP (3h) : PDF + JAVA dans « sprint2 »
■ Compl´ ements « Pour aller plus loin » sur les lambda expressions et les Streams
36/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset
R´ ef´ erences I
Bloch, J. (2018).
Effective Java, 3rd Edition.
Addison-Wesley.
Class Optional (JSE8).
Javadoc of the class java.util.Optional of JAVA 9.
https:
//docs.oracle.com/javase/9/docs/api/index.html?java/util/Optional.html.
Kernighan, B. and Pike, R., editors (1984).
The UNIX Programming Environment.
Prentice-Hall.
Naftalin, M. (2015).
Mastering Lambdas : Java Programming in a Multicore World.
Mc Graw Hill, Oracle Press.
Naftalin, M. (2012).
The Lambda FAQ.
http://www.lambdafaq.org/.
37/38 01/2022 Denis Conan CSC4102 :Lambda expressions,StreamsetOptional
R´ ef´ erences II
PackageJavaUtilFunction (JSE8).
Javadoc of the package java.util.function of JAVA SE 8.
https://docs.oracle.com/javase/9/docs/api/java/util/function/
package-summary.html.
Saumont, P.-Y. (2017).
Functional Programming in Java—How functional techniques improve your Java programs.
Manning.
Urma, R.-G. (2014).
Tired of Null Pointer Exceptions ? Consider Using Java SE 8’s Optional !
https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html.
38/38 01/2022 Denis Conan CSC4102 :Lambda expressions,Streamset