CSC4102 : Programmation orient e objet en JAVA - Lambda expressions, Streams, Optional Revision : 4084

La page est créée Patricia Gomez
 
CONTINUER À LIRE
CSC4102 : Programmation orient e objet en JAVA - Lambda expressions, Streams, Optional Revision : 4084
CSC4102 : Programmation
orienté objet en
JAVA — Lambda
expressions, Streams,
Optional
Revision : 4084

Denis Conan
Janvier 2019
Sommaire

       1. Motivations et objectifs

       2. Lambda expressions JAVA

       3. Utilisation dans la manipulation des collections : les Streams

       4. Utilisation dans la gestion des références null : Optional

       5. Streams + Optional

       6. Mise en pratique en TP (2h) + HP (3h)

          . Certaines sections de cette présentation sont insérées pour complétude dans
       l’optique de servir de référence (pour des approfondissements ultérieurs). Ces sections
       sont marquées d’un astérisque (« * ») dans leur titre.
2/45    01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
1 Motivations et objectifs

       1.1   Qui demande ?
       1.2   Pourquoi des fonctions ?
       1.3   Pourquoi des lambda expressions JAVA ?*
       1.4   Quels sont les objectifs de la séance ?

3/45    01/2019          Denis Conan          CSC4102 : Lambda expressions, Streams et Optional
1.1 Qui demande ?
       Brian Goetz, Java Language Architect, Oracle
       Specification lead for JSR-335 (Lambda Expressions for the Java Programming Language)
       Foreword of [Naftalin, 2015]

        Héritage et type paramétré = abstractions pour les données et les méthodes

        Lambda expression = abstraction pour les fonctions

        •    2009 : lancement du projet Lambda
        •    2006 : propositions pour ajouter le concept de « clôture »/« fermeture »
             (closure)
        •    1997 : ajout du concept de « classe anonyme » (anonymous inner class)
        •    1941 : travaux d’Alonzo Church sur la théorie du calcul,
             d’où vient la terminologie

        Ce sont les premiers pas vers la programmation orientée fonction,
          appelée aussi programmation fonctionnelle

4/45     01/2019               Denis Conan                  CSC4102 : Lambda expressions, Streams et Optional
1.2 Pourquoi des fonctions ?
        Exemple 1 : pour programmer des réactions
          à des événements d’IHM
  myComboBox.setOnAction((event) -> {
    Person selectedPerson = myComboBox.getSelectionModel()
                                      .getSelectedItem();
    outputTextArea.appendText("ComboBox Action (selected: "
                              + selectedPerson.toString());
                              + ")\n");
  });

        Exemple 2 : pour parallèliser des actions
          sur des grandes collections de donnnées
                                                                                        fork
  double maxDistance =
      pointList.parallelStream()
               .map(p -> p.distance(0, 0))
                                                                       fork                            fork
               .reduce(Double::max)                            map             map               map           map
               .orElse(0.0);
                                                                        join    fonctions               join
                                                                     reduce                            reduce
                                                                                        join
                                                                                      reduce

5/45     01/2019         Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
1.3. Pourquoi des lambda expressions JAVA ?* I
       Voici un scénario extrait de [Naftalin, 2015] pour expliquer l’intérêt des lambda expressions

   1. Commençons avec un code qui itère sur une collection d’objets modifiables

          List pointList = Arrays.asList(new Point(1,2), new Point(2,4));
          for(Point p : pointList) { p.translate(1,1); }

          La ligne 2 de ce code est traduit par le compilateur en le code qui suit :

          Iterator pointItr = pointList.iterator();
          while (pointItr.hasNext()) { ((Point) pointItr.next()).translate(1,1); }

          Problème : le compilateur génère du code qui sera toujours séquentiel
          Nous souhaiterions une exécution qui puisse être parallèle à la demande,
          c’est-à-dire selon la mise en œuvre de la méthode forEach
          D’où, nous souhaitons quelque chose comme ceci :

          List pointList = Arrays.asList(new Point(1,2), new Point(2,4));
          pointList.forEach(/*translation d’un point selon le vecteur (1,1)*/);

6/45     01/2019                  Denis Conan                      CSC4102 : Lambda expressions, Streams et Optional
1.3. Pourquoi des lambda expressions JAVA ?* II
   2. Solution (avec le patron de conception « Commande »)

        // cette interface est définie dans java.lang (présentation simplifiée ici)
        public interface Iterable { ...
          void forEach(Consumer
1.3. Pourquoi des lambda expressions JAVA ?* III
   3. Remplacement de la classe anonyme par une lambda expression
       3.1 Le compilateur devrait pouvoir inférer l’interface attendu par forEach
            pointList.forEach(new Consumer() {
               public void accept(Point p) { p.translate(1,1); }
            });

       3.2 Avec le nom de l’interface attendue, le compilateur devrait pouvoir inférer le
           nom de la méthode
            pointList.forEach(             Point
               public void accept(Point p) { p.translate(1,1); }
             );

       3.3 Le type de l’argument peut alors être inféré du type appelant (pointList)
            pointList.forEach(                Point
                                    Point p     p.translate(1,1)
              );

       3.4 Avec une syntaxe particulière (->), cela donne une lambda expression
            pointList.forEach(p -> p.translate(1, 1));

8/45    01/2019             Denis Conan                  CSC4102 : Lambda expressions, Streams et Optional
1.4 Quels sont les objectifs de la séance ?

        Concept de lambda expression en JAVA

        Utilisation dans la manipulation des collections avec les Streams JAVA

        Utilisation dans la gestion des références null : Optional

        Ce sont les premiers pas vers la programmation orientée fonction
          (dite aussi programmation fonctionnelle)
        Mais nous n’étudions pas la programmation orientée fonction à proprement
          parler dans CSC4102
        •    Pour découvrir ce sujet, p.ex. :
             P.-Y. Saumont, Functional Programming in Java—How functional techniques
             improve your Java programs, [Saumont, 2017]

9/45     01/2019          Denis Conan           CSC4102 : Lambda expressions, Streams et Optional
2 Lambda expressions JAVA

        2.1   Définition et syntaxe
        2.2   Exemples de lambda expressions
        2.3   Contexte d’exécution d’une lambda expression
        2.4   Référence de méthode*
        2.5   Interfaces fonctionnelles standard*

           . Les éléments de cette section sont extraits des références      [Naftalin, M., 2012],
        [Naftalin, 2015],
                        et [Bloch, 2018]
10/45     01/2019             Denis Conan                CSC4102 : Lambda expressions, Streams et Optional
2.1 Définition et syntaxe
     Lambda expression = conceptuellement une fonction
        •     Arguments en entrée et valeur de retour
     Syntaxe
        •     Soit : (parameters) -> expression
        •     Soit : (parameters) -> { statements; }
     C’est un objet = une instance d’une interface (fonctionnelle1 )
        Runnable r = () -> {}; // crée une lambda expression
                               // et affecte une référence vers cette lambda expression à r
        Object o = r; // transtypage vers le haut, comme pour une référence/un objet

     Une lambda peut être utilisée là où une interface fonctionnelle est déclarée
        •     Interface fonctionnelle =⇒ une méthode =⇒ pas d’ambiguı̈té
        •     P.ex., « public void forEach(Consumer
2.2 Exemples de lambda expressions

1       (int x, int y) -> x + y                                           // retourne la somme des deux arguments
2       (x, y) -> x - y                                                   // retourne la différence
3       () -> 42                                                          // pas d’argument, retourne 42
4       (String s) -> System.out.println(s)                               // affiche l’argument et ne retourne rien
5       x -> 2 * x                                                        // un argument (sans parenthèses)
6       c -> { int s = c.size(); c.clear(); return s; }                   // type de l’argument possédant les méthodes
                                                                             size() et clear(), p.ex. une collection
     Types des arguments explicites (1, 4) ou inférés (2, 5, 6)

        •    Pas de mélange entre « explicite » et « inféré »
     Le corps peut être 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èses

12/45    01/2019            Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
2.3 Contexte d’exécution d’une lambda expres-
                   sion
     Une lambda expression s’exécute 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ée dans une instance de classe
     Règles classiques d’utilisation et de nommage des éléments du contexte

        • Un exemple légal : l’argument « i » de la lambda cache l’attribut « i »
         − class Bar { int i; Foo foo = i -> i * 2; };
        • Un exemple illégal : « i » est déjà une variable locale de « bar »
         − void bar() { int i; Foo foo = i -> i * 2; };

13/45    01/2019          Denis Conan            CSC4102 : Lambda expressions, Streams et Optional
2.4 Référence de méthode*
     Cas particulier d’une lambda expression avec un seul argument
         + l’expression est un unique appel à une méthode avec un seul argument
        • Par exemple : str -> Integer.parseInt(str)
        • Simplification de l’écriture avec la forme « référence de méthode »
         − Pour le même exemple : Integer::parseInt
     Plus généralement, une lambda expression peut être représentée par une
         méthode concrète d’une classe
          =⇒ Une référence de méthode est un raccourci d’écriture d’une lambda
         expression avec un argument et l’expression formée de l’appel unique de la
         méthode référencée
     Syntaxe (plus d’informations dans la diapositive suivante)
        •    ReferenceType::Identifier pour méthode de classe,
             p.ex. « Integer::parseInt »
        •    ObjectReference::Identifier pour méthode d’instance :
             p.ex. « System.out::println »
        •    ReferenceType::new pour constructeur, p.ex. « ArrayList::new »
14/45    01/2019           Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
2.4.1 Différents types de réf. de méthodes*
              Forme                                Forme                                           Type de
              « lambda expression »                « référence de méthode »                     méthode
                                                                                                   de référence
        1     str -> Integer.parseInt(str)         Integer::parseInt                               Statique
        2     Instant then = Instant.now();        Instant.now()::isAfter                          Lié
              t -> then.isAfter(t)                                                                 (bound)
        3     str -> str.toLowerCase()             String::toLowerCase                             Libre
                                                                                                   (unbound)
        4     () -> new TreeMap               TreeMap::new                               Constructeur
                                                                                                   de classe
        5     len -> int[len]                      int[]::new                                      Constructeur
                                                                                                   de tableau

     Les références de méthodes sont encore plus succinctes que les lamdda
        •   Des outils comme SpotBugs et SonarLint dans Eclipse proposent
            systématiquement les remplacements
        •   Lors du remplacement de la lambda par la référence, attention aux références
            de méthodes avec variables liées (#2) : variable « then » définie avant la
            définition de la lambda expression Versus méthode « Instant.now() »
            appelée dans la définition de la référence de méthode (cf. un exemple dans la
            diapositive qui suit)
15/45   01/2019            Denis Conan              CSC4102 : Lambda expressions, Streams et Optional
2.4.2 Exemples de références de méthodes
        Classe seance8/LambdaVersusReferenceDeMethode

   1    ToIntFunction < String > f1L = str -> Integer . parseInt ( str ) ;
   2    ToIntFunction < String > f1RM = Integer :: parseInt ;
   3    // essai de f1L et f1RM
   4    System . out . println ( f1L . applyAsInt ( " 1 " ) + " ␣ " + f1RM . applyAsInt ( " 1 " ) ) ;
   5    Instant then = Instant . now () ;
   6    Predicate < Instant > f2L = t -> then . isAfter ( t ) ;
   7    Predicate < Instant > f2RM = Instant . now () :: isAfter ;
   8    // essai de f2L et f2RM + idem en lambdas
   9    System . out . println ( f2L . test ( then ) + " ␣ " + f2RM . test ( then ) ) ;
  10    System . out . println ( then . isAfter ( then ) + " ␣ " + Instant . now () . isAfter ( then ) ) ;
  11    UnaryOperator < String > f3L = str -> str . toLowerCase () ;
  12    UnaryOperator < String > f3RM = String :: toLowerCase ;
  13    // essai f3L et f3RM
  14    System . out . println ( f3L . apply ( " ABC " ) + " ␣ " + f3RM . apply ( " ABC " ) ) ;
  15    Supplier < TreeMap < String , String > > f4L = () -> new TreeMap < String , String >() ;
  16    Supplier < TreeMap < String , String > > f4RM = TreeMap :: new ;
  17    // essai f4L et f4RM
  18    System . out . println ( f4L . get () + " ␣ " + f4RM . get () ) ;
  19    IntFunction < int [] > f5L = len -> new int [ len ];
  20    IntFunction < int [] > f5RM = int []:: new ;
  21    // essaie f5L et f5RM
  22    System . out . println ( f5L . apply (4) . length + " ␣ " + f5RM . apply (4) . length ) ;

        Affichage : « 1 1 », puis « false true », « false true », « abc abc », « {} {} », et enfin « 4 4 »

16/45     01/2019                Denis Conan                   CSC4102 : Lambda expressions, Streams et Optional
2.5 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                                Exemple
                                               la fonction
        1      UnaryOperator                T apply(T t)                             String::toLowerCase
        2      BinaryOperator               T apply(T t1, T, t2)                     BigInteger::add
        3      Predicate                    boolean test(T t)                        Collection::isEmpty
        4      Function                  R apply(T t)                             Arrays::asList
        5      Supplier                     T get()                                  Instant::now
        6      Consumer                     void accept(T t)                         System.out::println
        7      ToIntFunction                int applyAsInt(T t)                      Integer::parseInt
        8      IntFunction                  R apply(int i)                           int[]::new

17/45    01/2019           Denis Conan                      CSC4102 : Lambda expressions, Streams et Optional
2.5.1 Exemples d’utilisation

        Classe seance8/InterfaceFonctionnelleStandard

   1    // interfaces fon ction nelles de base
   2    UnaryOperator < String > f1 = String :: toLowerCase ;
   3    System . out . println ( f1 . apply ( " ABC " ) ) ; //
   4    BinaryOperator < BigInteger > f2 = BigInteger :: add ;
   5    BigInteger a = new BigInteger ( " 2 " ) , b = new BigInteger ( " 3 " ) ;
   6    System . out . println ( f2 . apply (a , b ) ) ; //
   7    Predicate < Collection < String > > f3 = Collection < String >:: isEmpty ;
   8    System . out . println ( f3 . test ( new ArrayList < String >() ) ) ; //
   9    Function < String [] , List < String > > f4 = Arrays :: asList ;
  10    System . out . println ( f4 . apply ( new String [] { " a " , " b " }) ) ; //
  11    Supplier < Instant > f5 = Instant :: now ;
  12    System . out . println ( f5 . get () ) ; //
  13    Consumer < String > f6 = System . out :: println ;
  14    // variante pour les types primitifs
  15    ToIntFunction < String > f7 = Integer :: parseInt ;
  16    IntFunction < int [] > f8 = int []:: new ;
  17    f6 . accept ( " abc " ) ; System . out . println ( f7 . applyAsInt ( " 11 " ) ) ; //

        Affichage :
        « abc », puis « 5 », « true », « [a, b] », « 2019-03-13T21:06:26.529Z », « abc », « 11 », et enfin « 4 »

18/45     01/2019                Denis Conan                   CSC4102 : Lambda expressions, Streams et Optional
3 Utilisation dans la manipulation des collections :
                    les Streams

        3.1   Retour sur les motivations et les objectifs des lambda expressions
        3.2   Pipeline, stream, et évaluation tardive
        3.3   Début du pipeline
        3.4   Traitements intermédiaires du pipeline
        3.5   Terminaison d’un pipeline
        3.6   Quelques exemples

           . Les éléments de cette section sont extraits des références      [Naftalin, M., 2012],
        [Naftalin, 2015],
                        et [Bloch, 2018]
19/45     01/2019             Denis Conan                CSC4102 : Lambda expressions, Streams et Optional
3.1 Retour sur les motivations et les objectifs des
                  lambda expressions
     Motivation 1 : introduire des élements de programmation orientés fonction

     Motivation 2 : raccourcir l’écriture avec le nouveau sucre syntaxique « -> »
     Motivation 3 : permettre des exécutions parallèles
        •   C’est cette motivation qui donne la bibliothèque 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 »

                                                                                       fork
  double maxDistance =
      pointList.parallelStream()                                      fork                            fork
               .map(p -> p.distance(0, 0))                    map             map               map           map
               .reduce(Double::max)
                                                                       join                            join
               .orElse(0.0);
                                                                    reduce                            reduce
                                                                                       join
                                                                                     reduce

20/45   01/2019             Denis Conan           CSC4102 : Lambda expressions, Streams et Optional
3.2 Pipeline, stream, et évaluation 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 à granularité fine et composables
     Stream = Séquence de valeurs, ordonnées ou non, et non stockées
     Pipeline de streams + évaluation tardive
        • C’est l’opération terminale qui « tire » les valeurs
         − Aucune valeur n’est calculée tant qu’elle n’est pas demandée
         IntStream.iterate(1, i -> i * 2) // génération stream de taille infinie
                  .limit(10) // limitation du nombre de générations
                  .forEach(System.out::println) // opération qui « tire » les valeurs

     Toutes les valeurs ne sont peut-être pas calculées
         P.ex., IntStream.iterate(1, i -> i * 2).limit(10).anyMatch(v -> v == 128)

21/45    01/2019           Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
3.3 Début du pipeline

     IntStream, etc. pour éviter les opérations de conversion entre le type
        conteneur Integer et le type primitif int
        + méthodes generate, iterate, range, of, etc. pour démarrer le pipeline
        •   P.ex., IntStream.iterate(1, i -> i * 2), IntStream.range(7, 42)
     Interface Collection maintenant avec les méthodes stream et
        parallelStream qui créent un stream
        •   P.ex., maListe.stream()
        •   Dans CSC4102, nous n’utiliserons pas de parallélisation
     Plus rarement, classe Stream avec les méthodes de classe of, empty, etc.

        •   P.ex., Stream.of(new Point(1, 2), new Point(2, 4))

22/45   01/2019           Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
3.4 Traitements intermédiaires du pipeline

     Transformation = retourne un stream d’un autre type d’éléments

        •     Stream map(Function mapper) et flatMap
        •     DoubleStream mapToDouble(ToDoubleFunction mapper), etc.

     Filtrage = Stream filter(Predicate predicate), dropWhile

     Combinaison de streams : concat

     Tri (sorted), suppression des doublons (distinct), troncature (limit et skip)

     Introspection pour le déverminage = même séquence en sortie

          + instertion d’un traitement avec peek

        IntStream.iterate(1, i -> i*2).limit(10).peek(System.out::println).anyMatch(v -> v==128);

23/45     01/2019            Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
3.5 Terminaison d’un pipeline
     Recherche d’un élément et test de présence :
        •   Optional findAny2 , findFirst
        •   boolean anyMatch(Predicate), allMatch, et noneMatch
     Réduction :
        •   Calcul : int sum(), Optional min(), Optional max(), long count(),
            Optional average(), IntSummaryStatistics summaryStatistics
        •   Collecte : R collect(Collector
3.6 Quelques exemples I

        Début : génération d’une séquence infinie avec IntStream
             Intermédiaire : troncature avec limit + insertion déverminage avec peek
             Terminaison : test de présence avec anyMatch
        Démonstration de l’évaluation tardive

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    IntStream . iterate (1 , i -> i * 2)
   2              . limit (10)
   3              . peek ( System . out :: println )
   4              . anyMatch ( v -> v == 16) ;

        Affichage :
        1
        2
        4
        8
        16

25/45        01/2019          Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples II
        Début : Collection::stream
          Intermédiaire : filtrage avec filter
          Terminaison : recherche d’un élément avec findFirst
        Recherche dans une collection avec filtrage
          + obtention de la première valeur trouvée
        •     Hypothèse : la précondition du cas d’utilisation « ajouter un genre » inclut la
              condition « genre avec ce nom inexistant »

        Classe etudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque

   1                private Optional < Genre > chercherGenre ( final String nomGenre ) {
   2                        return lesGenres . stream () . filter ( g -> g . getNom () . equals (
                                 nomGenre ) ) . findFirst () ;
   3                }
   4
   5                /* *
   6                 * sup prime rGenr e permet de supprimer un Genre dans la liste des
                           genres .

        La classe Optional est étudiée dans la section qui suit celle-ci.

26/45     01/2019              Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples III
        Terminaison : test de présence avec anyMatch

        Existe-t-il un document avec le genre donné ?

        Classe etudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque

   1                }
   2
   3                /* *

27/45     01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples IV
        Début : génération d’une séquence infinie avec IntStream
          Intermédiaire : troncature avec limit + transformation avec mapToObj
          Terminaison : réduction avec collect en une liste avec Collectors::toList
        Collecte dans une liste

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    List < String > l = IntStream . iterate (1 , i -> i * 2)
   2                                   . limit (10)
   3                                   . mapToObj ( String :: valueOf )
   4                                   . collect ( Collectors . toList () ) ;
   5    System . out . println ( l ) ;

        Affichage :
        [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

        Cet exemple démontre implicitement une construction d’objet

        •     On pourrait écrire mapToObj(Integer::new) pour récupérer une List

28/45     01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples V
        Collecte dans une chaı̂ne de caractères avec Collectors::joining

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    String s = IntStream . iterate (1 , i -> i * 2)
   2                               . limit (10)
   3                               . mapToObj ( String :: valueOf )
   4                               . collect ( Collectors . joining ( " ␣ + ␣ " ) ) ;
   5    System . out . println ( s ) ;

        Affichage :
        1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512

29/45     01/2019               Denis Conan                   CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples VI
        Début : génération de deux streams
          Intermédiaire : concaténation des deux streams avec Stream::concat
          + retrait des doublons avec distinct
          Terminaison : collecte dans un ensemble avec toSet
        Démonstration de la concaténation de streams

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    List < List < String > > listeDeListes = new ArrayList < >() ;
   2    listeDeListes . add ( Arrays . asList ( " a " , " b " , " c " ) ) ;
   3    listeDeListes . add ( Arrays . asList ( " c " , " d " , " a " ) ) ;
   4    Set < String > concat = Stream . concat ( listeDeListes . get (0) . stream () ,
   5                                                  listeDeListes . get (1) . stream () )
   6                                        . distinct ()
   7                                        . collect ( Collectors . toSet () ) ;
   8    System . out . println ( concat ) ;

        Affichage :
        [a, b, c, d]

30/45     01/2019             Denis Conan                 CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples VII
        Même exemple avec généralisation à un nombre quelconque de listes

        •     Début : obtention de la séquence de listes de la liste englobante
              Intermédiaire : transformation de chaque liste de String en une stream avec
              List::stream et insertion dans une séquence de String
           −        flatMap = flattened map
                    • Retourne un Stream au lieu d’un Stream

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    List < List < String > > listeDeListes = new ArrayList < >() ;
   2    listeDeListes . add ( Arrays . asList ( " a " , " b " , " c " ) ) ;
   3    listeDeListes . add ( Arrays . asList ( " c " , " d " , " a " ) ) ;
   4    Set < String > flatMap = listeDeListes . stream ()
   5                                                  . flatMap ( List :: stream )
   6                                                  . distinct ()
   7                                                  . collect ( Collectors . toSet () ) ;
   8    System . out . println ( flatMap ) ;

31/45     01/2019              Denis Conan                 CSC4102 : Lambda expressions, Streams et Optional
3.6 Quelques exemples VIII

        Début : génération d’un streams
          Terminaison : application d’un effet de bord avec forEach
        Exemple d’application d’un effet de bord

        Classe seance8/lambdaexpressions/ExampleNaftalin2015

   1    List < Point > points = Arrays . asList ( new Point (1 , 2) , new Point (2 , 4) ) ;
   2    System . out . println ( points ) ;
   3    points . stream () . forEach ( p -> p . translate (1 , 2) ) ;
   4    System . out . println ( points ) ;

        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]]

32/45     01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
4 Utilisation dans la gestion des références null :
                   Optional

        4.1 Problème des références null
        4.2 Solution non satisfaisante : documentation
        4.3 Solution/idiome JAVA : classe Optional

33/45    01/2019          Denis Conan          CSC4102 : Lambda expressions, Streams et Optional
4.1 Problème des références null

        Situations dans lesquelles « pointeur null » signifie « absence de valeur »

        •     P.ex. valeur de retour pouvant être null
                                                                                                       SousGenre
        Classe seance8/lambdaoptional/DocumentSansOptional                                        nom:string
                                                                                                  nbEmprunts:integer=0
   1    public String ge t No m So u sG enre () {
                                                                                                                         0..1

                                                                                                            possède
   2      String result = " inconnu " ;
   3      if ( genre != null ) { // attribut pouvant etre null
   4        SousGenre sg = genre . getSousGenre () ; // peut retourner null
   5        if ( sg != null ) {                                                                            Genre
                                                                                                  nom:string
   6           String n = sg . getNom () ;                                                        nbEmprunts:integer=0
   7           if ( n != null ) { // inutile si verifie dans l ’ invariant

                                                                                                            correspond
   8             result = n ;                                                                                            0..1
   9           }
  10        }                                                                                                            *
  11      }                                                                                                 Document
  12      return result ;                                                                         code:string
  13    }                                                                                         titre:string
                                                                                                  auteur:string
                                                                                                   getSousGenre()

34/45     01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
4.2 Solution non satisfaisante : documentation

        Besoin d’avertir le programmeur de la méthode appelante

        •     Premier essai : utiliser la documentation Javadoc

        Classe seance8/lambdaoptional/GenreSansOptional

   1    /* *
   2      * obtient le sous - genre .
   3      *
   4      * @return le sous - genre , qui peut etre { @code null }.
   5      */
   6    public SousGenre getSousGenre () {
   7        return sousGenre ;
   8    }

35/45     01/2019             Denis Conan                 CSC4102 : Lambda expressions, Streams et Optional
4.3 Solution/idiome JAVA : classe Optional

     La solution/l’alertidiome JAVA :

        •    Similairement à d’autres langages comme Groovy ou Scala,
             JAVA propose depuis la version 8 un idiome pour rendre explicite la possibilité
             qu’une référence puisse être null
             et ainsi alerter le programmeur pour qu’il evite le déréférencement dans ce cas
         − P.ex. : Optional getSousGenre()
        • Pour votre culture générale, conférence de C.A.R. Hoare,
             qui en 1965 a introduit le concept de « référence null »,
             ce qu’il considère maintenant comme une erreur :
          −        « Null References: The Billion Dollar Mistake »
                   https://www.infoq.com/presentations/
                   Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

36/45    01/2019              Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
4.3.1 Classe Optional
     Classe conteneur et paramétrée par le type du contenu : Optional
     Pas de constructeur, mais création par méthodes de classes dite
        « fabriques » (factories)
        •   Optional g = Optional.of(genre) :
            genre = null =⇒ exception NullPointerException
        •   Optional g = Optional.ofNullable(genre) : valeur null possible
        •   Optional g = Optional.empty() : contient null
     Des méthodes pour récupérer la valeur ou tester si null
        •   g.get() : lève l’exception NoSuchElementException si contenu null
        •   g.isPresent() : retourne false si contenu null
        •   g.orElse("inconnu") : retourne contenu non null, sinon "inconnu"
     Des méthodes 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)
        •   sg.flatMap(SousGenre::getNom) : idem map, mais sans mettre le résultat dans
            un Optional (p.ex. ici retourne un String)

37/45   01/2019           Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
4.3.2 Où 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 (par un cas d’erreur)
        Classe seance8/lambdaoptional/GenreAvecOptional

   1    /* *
   2      * obtient le sous - genre .
   3      *
   4      * Le diagramme de classes specifie qu ’ un sous - genre est optionnel .
   5      *
   6      * @return le sous - genre , qui peut etre { @code null }.
   7      */
   8    public Optional < SousGenre > getSousGenre () {
   9        return Optional . ofNullable ( sousGenre ) ;
  10    }

38/45     01/2019             Denis Conan                 CSC4102 : Lambda expressions, Streams et Optional
4.3.3 Manipulation d’un objet Optional (1/2)

        Même exemple qu’au début de la section avec
          Même structure : imbrication de if
        •     isPresent : retourne true si un objet est présent
        •     get : retourne la valeur présente, sinon lève l’exception
              NoSuchElementException

        Classe seance8/lambdaoptional/DocumentAvecOptional1

   1    public String ge t No m So u sG enre () {
   2      String result = " inconnu " ;
   3      if ( getGenre () . isPresent () ) {
   4        Optional < SousGenre > sg = genre . getSousGenre () ;
   5        if ( sg . isPresent () ) {
   6           String n = sg . get () . getNom () ;
   7           if ( n != null ) { // inutile si dans l ’ invariant
   8             result = n ;
   9           }
  10        }
  11      }
  12      return result ;
  13    }

39/45     01/2019             Denis Conan               CSC4102 : Lambda expressions, Streams et Optional
4.3.4 Manipulation d’un objet de Optional (2/2)

        Utilisation de flatMap, map, et orElse
        •     GenreAvecOptional::getSousGenre retourne un Optional
        •     D’où, getGenre().map(GenreAvecOptional::getSousGenre)
              retournerait un objet de type Optional
        •     Donc, flatMap pour obtenir un objet de type Optional au lieu
              d’un objet de type Optional
           −        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ésente (≡ get), sinon une valeur par défaut
        Classe seance8/lambdaoptional/DocumentAvecOptional2

   1    public String ge t No m So u sG enre () {
   2      return getGenre ()
   3             . flatMap ( G e n r e A v ec Opt io nal :: getSousGenre )
   4             . map ( SousGenre :: getNom )
   5             . orElse ( " inconnu " ) ;
   6    }

40/45     01/2019               Denis Conan                 CSC4102 : Lambda expressions, Streams et Optional
5 Streams + Optional
        Règle 45 de « Effective JAVA »         [Bloch, 2018]   : « Use streams judiciously »
        •     « Overusing streams makes programs hard to read and maintain »
        Classe etudesdecas/mediathequeaveclambdasoptionaletstreams/Mediatheque
   1                                           . stream () . filter (
   2                                                                f -> f . correspond (
   3                                                                                  lesClients .
                                                                                           values ()
                                                                                           . stream
                                                                                           ()
   4                                                                                                         .

41/45     01/2019             Denis Conan                CSC4102 : Lambda expressions, Streams et Optional
6 Mise en pratique en TP (2h) + HP (3h)

     Utilisation des Streams et de Optional dans certaines parties du code

     Continuation du développement de l’application de l’étude de cas

     Organisation du TP (2h)

        •   1h : Travail en binôme-projet
        •   2 × 5mn : Évaluation croisée entre binômes-projets + bilan intermédiaire
            collectif
        •   Reste de la séance : continuation des travaux

     Rendu de la séance en HP (3h) : PDF + JAVA, avec « seance8 » dans
        « sprint2 »

42/45   01/2019            Denis Conan             CSC4102 : Lambda expressions, Streams et Optional
Références I

        Bloch, J. (2018).
        Effective Java, 3nd Edition.
        Addison-Wesley.

        Class Collectors (JSE8).
        Javadoc of the class java.util.stream.Collectors of JAVA 9.
        https:
        //docs.oracle.com/javase/9/docs/api/java/util/stream/Collectors.html.

        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.

43/45   01/2019            Denis Conan                CSC4102 : Lambda expressions, Streams et Optional
Références II

        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/.

        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.

44/45   01/2019          Denis Conan            CSC4102 : Lambda expressions, Streams et Optional
Références III

        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.

45/45   01/2019          Denis Conan         CSC4102 : Lambda expressions, Streams et Optional
Vous pouvez aussi lire