Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Librairies Java qui changent la vie

222 vues

Publié le

Vavr et Concordion sont deux librairies Java qui apportent beaucoup au quotidien.

Publié dans : Logiciels
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Librairies Java qui changent la vie

  1. 1. Librairies Java qui changent la vie Joseph Pachod https://twitter.com/joeclueless
  2. 2. Disclaimer Beaucoup plus de contenu que prévu… Uniquement vavr et concordion dans un premier temps...
  3. 3. Une présentation java.Time ? Qui connaît ? Qui utilise ? Equivalent du jdk8 et plus de la librairie JodaTime, java.Time adresse enfin correctement les problématiques de fuseau horaire, décalage, date sans horaire et autres horaires sans date, le tout avec ce qu’il faut d’utilitaires et de conversions pour un usage courant au top :) Qui aimerait une présentation ?
  4. 4. Une présentation Lombok ? Qui connaît ? Qui utilise ? Lombok : génération de code lors de la compilation via des annotations, éviter de toujours coder/maintenir soit même les accesseurs, toString, equals, logger… Notion très pratique d’objets de données, via un regroupement de plein de choses utiles dans une même implémentation : @Data Qui aimerait une présentation ?
  5. 5. Une présentation jOOQ ? Qui connaît ? Qui utilise ? JOOQ considère que le SQL est un langage très riche et intéressant. Aussi, pour faciliter son usage depuis Java il propose un DSL pour écrire du SQL mettant en avant toutes les possibilités de SQL. Ainsi on apprend toujours quelque chose mais surtout notre SQL est fortement type et peut aller d’un select à un create table… Dès que possible jOOQ implémente les fonctionnalités de façon sur plusieurs implémentations de SQL, sinon il fait quand même et précise les limites de portabilité (en permettant de s’assurer de la compatibilité via annotation si besoin). JOOQ peut s’utiliser seul ou via Hibernate. Qui aimerait une présentation ?
  6. 6. Présentation forte en lambda... Si vous ne savez pas lire du code contenant des lambdas, ça va être dur. Révisez un coup puis revenez ^^
  7. 7. L’association IPA API anyone ? Rien à voir!
  8. 8. Informaticiens Protecteurs des Animaux En anglais : "Yeeepa !" C’est débile clairement, mais c’est pour le contexte fonctionnel ^^
  9. 9. Good code? Le code doit être comme un bon chien. Du good code quoi. Certains disent Clean code, mais on lave tous nos animaux. Anyway, c'est quoi du good code?
  10. 10. Un bon chien aide son propriétaire, maintenant et dans le futur. Le good code, c'est comme un bon chien.
  11. 11. Permettre les développements futurs. Voir même les rendre plus aisés. Plus on avance, plus on doit aller vite. Idée folle ?
  12. 12. objectif : enregistrement des propriétaires d'animaux nom et date de naissance (doivent être adultes) Commençons par la date de naissance
  13. 13. LocalDate parse(String date){ return LocalDate.parse( date, DateTimeFormatter.ofPattern("yyyy/MM/dd")); } Un problème? Est ce du good code? Quid d’une erreur de parsing ? Exception !
  14. 14. LocalDate parse(String date){ try { return LocalDate.parse( date, DateTimeFormatter.ofPattern("yyyy/MM/dd")); } catch (Exception e) { return ???; } } Rethrow - si unchecked, c'est l'origine du problème - si checked, c'est un autre problème… Et personne n’a recopié cette fonctionnalité Java, les exceptions vérifiées, depuis son invention… Passons donc ! Bref, que retourner ?
  15. 15. Option to the rescue! Option<LocalDate> parse(String date){ try { return Option.of( LocalDate.parse( date, DateTimeFormatter .ofPattern("yyyy/MM/dd"))); } catch (Exception e) { return Option.none(); } } Pour l’instant, disons que l’Option présente dans le code fait la même chose que l’Optional de la librairie standard Java. Et l’essentiel est que le boulot est fait !
  16. 16. Option<Integer> validateAge(String birthdate) { return parse(birthdate) .map( d -> Period.between( d, LocalDate.now()) .getYears()) .filter(age -> age >= MIN_AGE); } All good? I may want to log the exception... Optional is not enough, let's keep the exception. En fait, il s'agit d'un essai de parsing n'est ce pas ?
  17. 17. Try parse(String date) { try { return new Success( LocalDate.parse( date, DateTimeFormatter .ofPattern("yyyy/MM/dd"))); } catch (Exception t) { return new Failure(t); } } Success et Failure implémentent tous deux une interface Try reprenant l'essentiel d'Option(al). On stocke désormais l'exception si une est présente et on indique qu'il s'agit d'un échec. Et on permet d'accéder au contenu de l'erreur.
  18. 18. Try onFailure(Consumer<? super Throwable> action) { Objects.requireNonNull( action, "action is null"); if (isFailure()) { action.accept(getCause()); } return this; } onFailure retourne le try lui même, permettant de chaîner Et si pas failure l’action communiquée n’est pas exécutée. Facile !
  19. 19. parse(birthdate) .onFailure( t -> log.debug("Parsing failure for birthdate '" + birthdate + "'", t)) .map( d -> Period.between(d, LocalDate.now()).getYears()) .filter(age -> age >= MIN_AGE); // gives a Try Le code reste grosso modo le même : on ne gère que ce qui se passe bien. On ajoute juste le log. Une ligne. Pas de if. How great is that ?
  20. 20. Lancer des exceptions est fréquent! Division (par zéro) Integer.parseInt(string) Code business... File...
  21. 21. static <T> Try<T> of( CheckedFunction0<? extends T> supplier) { Objects.requireNonNull( supplier, "supplier is null"); try { return new Success<>(supplier.apply()); } catch (Throwable t) { return new Failure<>(t); } } Le Try de vavr : - générique pour un usage plus large :) - un supplier pour passer l’action pouvant lancer une exception :)
  22. 22. Try<T> onSuccess(Consumer<? super T> action) <U> Try<U> flatMap( Function<? super T,? extends Try<? extends U>> mapper) Try<T> andFinally(Runnable runnable) ... Bien plus dans le Try de vavr ! onSuccess, le pendant d'onFailure flatMap : permet de gérer un autre bout de code pouvant lancer des exceptions et permet de tenter autre chose si réussite du premier Try andFinally pour une action toujours réalisée quelque soit le résutlat du Try
  23. 23. List<T> toJavaList() Optional<T> toJavaOptional() List<T> toList() Option<T> toOption() ... Et en bonus plein de fonctions de conversions, juste un échantillon là. Et oui vavr a sa propre List et Option, on en parle plus tard :)
  24. 24. Try<LocalDate> parse(String date) { return Try.of( () -> LocalDate.parse( date, DateTimeFormatter .ofPattern("yyyy/MM/dd"))); } Parse avec Try de vavr
  25. 25. Try<Integer> validateAge(String birthdate) { return parse(birthdate) .onFailure( t -> log.debug("Parsing failure for birthdate " + birthdate, t)) .map(d -> Period.between( d, LocalDate.now()).getYears()) .filter(age -> age >= MIN_AGE); } Validation de l’âge avec le Try de vavr
  26. 26. Question de l'âge réglée, passons à la suite... :)
  27. 27. Contrôle du nom /** * Retourne un message si erreur. */ Option<String> checkName(String name) On doit utiliser une méthode pré existante qui retourne (ou pas) une erreur
  28. 28. Option<String> nomValide = Option.of(nom) .filter(n -> !n.trim().isEmpty()) .filter(n -> checkName(n).isEmpty(); Problème dans le code ci dessus : on perd les erreurs. Dur de les remonter à l’utilisateur !
  29. 29. Tuple2<String, String> t = Tuple.of("foo","bar"); t._1;// foo t._2;// bar Un tuple associe plusieurs valeurs, comme une Entry de Map en Java mais en plus souple (on peut créer soit même des entrées). Il existe de Tuple0 à Tuple8 Très pratique en plein d'occasions
  30. 30. Tuple2<String, Option<String>> validateNom( String nom) { return Option.of(nom) .filter(n -> !n.trim().isEmpty()) .map(n -> Tuple.of(n, Option.<String>none())) .getOrElse( Tuple .of( nom, Option.of("Le nom doit être renseigné avec des caractères alpha numériques"))); } On peut retourner la valeur et les éventuelles erreurs :) Mais… pas forcément évident de distinguer les deux… Et puis l’erreur à la fin est sacrément décorrélée de son origine...
  31. 31. Either<L,R> R pour Right : valeur correcte L pour Left : alternative(s) Transporte la bonne valeur, Right, ainsi que les éventuelles erreurs ou messages associés.
  32. 32. Option<Either<L, R>> filter(Predicate<? super R> predicate) <U> Either<L, U> map(Function<? super R, ? extends U> mapper) <U> Either<U, R> mapLeft( Function<? super L, ? extends U> leftMapper) <U> Either<L, U> flatMap( Function< ? super R, ? extends Either<L, ? extendsU>> mapper) Les signatures complexes ne doivent pas effrayer, il s’agit toujours des bonnes vieilles mêmes méthodes : filter, map et flatMap. Seule mapLeft est un peu différente, afin de mapper sur la valeur « left ».
  33. 33. static <L, R> Either<L, R> right(R right) static <L, R> Either<L, R> left(L left) On a également des constructeurs pour créer directement la valeur droite ou gauche
  34. 34. Conversions depuis Option/Try/... <L> Either<L, T> toEither(L left) <L> Either<L, T> toEither(Supplier<? extends L> leftSupplier) <R> Either<T, R> toLeft(R right) <R> Either<T, R> toLeft(Supplier<? extends R> right) <L> Either<L, T> toRight(L left) <L> Either<L, T> toRight(Supplier<? extends L> left)
  35. 35. Either<String, String> validateNom(String nom) { return Option.of(nom) .filter(n -> !n.trim().isEmpty()) .toEither("Le nom doit être renseigné avec des caractères alpha numériques") .flatMap(s -> { Option<String> errors = checkName(s); return errors.toLeft(s); }); } On utilise flatMap afin de pouvoir utiliser la valeur Right pour déduire la nouvelle valeur Left Comme précédemment, ce flatMap n’est appelé que si Either a la valeur Right. A défaut rien ne se passe et on demeure sur la valeur Left définie plus haut, à savoir « Le nom doit être renseigné avec des ... »
  36. 36. Either<String, Integer> validateAge(String Birthdate) { return parse(birthdate) .onFailure(t -> log.debug("Parsing failure for birthdate " + birthdate, t)) .toEither("La date de naissance doit être au format " + DATE_PATTERN + ".") .map(d -> Period.between( d, LocalDate.now()).getYears()) .flatMap(age -> (age >= MIN_AGE) ? Either.right(age) : Either.left("Il faut avoir plus de " + MIN_AGE)); } Ici le flatMap se fait sur une either, vu qu’on a besoin de l’age pour déterminer s’il est correct.
  37. 37. Either<String, Person> validatePerson( String name, String birthdate) { return validateNom(name) .flatMap(nomValide -> validateAge(birthdate) .map(age -> new Person(nomValide, age))); } Là on ne lève pas de nouveaux cas d’erreurs, du coup on se contente du flux normal. Joli non ? A noter qu’on peut aussi se servir d’Either pour une alternative (indépendamment de toute gestion d’erreur). Par exemple « Either<Cat,Dog> ». Autre usage souvent pratique :)
  38. 38. Option, Try et Either Beaucoup de ressemblances Plusieurs fois le même principe: une ou des valeurs sur lesquelles on applique des opérations. Expliciter le comportement du code
  39. 39. Option → Absence Try → Exception Either → Alternative Et tout cela à côté du cas normal Option, Try et Either explicitent des situations parfois cachées dans notre code Ainsi le code devient bien plus clair. Qui plus est, la façon d’encapsuler les éléments mis en avant, cad de n’appliquer les map ou flatMap que si la valeur est ok (cad non absente, sans exception et sans l’alternative) permet de faire un code très lisible, concis et sans if imbriqués. Du bonheur pour le lecteur :)
  40. 40. Monade un état des opérations dans la monade des opérations de sortie de la monade La ressemblance est plus profonde que juste des similitudes de comportement, ces 3 notions sont trois déclinaisons d’un même concept : la monade. - un état (un élément ou plusieurs) - des opérations "dans" la monade (map, filter....) -- "happy path" dans les monades vues jusqu'ici, on ne décrit que le "chemin nominal", on ignore soit les autres soit ils sont un état à part embarqué en même temps -- permet de différer le traitement des erreurs quand on veut, une fois à la fin généralement - des opérations de sortie de la monade (get...)
  41. 41. Monade Méthodes essentielles constructeur flatMap Avec le constructeur et flatMap on peut faire map, filter... En Haskell on appelle ces méthodes unit et bind
  42. 42. IO Monad :) En programmation fonctionnelle, on utilise une monade pour décrire les opérations d'IO : description dans la monade des opérations, déclenchement à la demande en sortant de la monade
  43. 43. Plus proche de nous, d’autre monades se cachent… Avez vous connaissance d’une autre Monade que vous utilisez au quotidien ? En Java..
  44. 44. List/Set/Map… du moins en Vavr !
  45. 45. Outils de programmation fonctionnelle... en Java! Avec vavr, c’est java à l’envers. Littéralement : inverser le logo ! On reste toutefois à des niveaux « simples » de PF, du fait notamment des limites du langage Mais c’est déjà beaucoup et permet d’améliorer sensiblement notre code.
  46. 46. Inspiré de Scala... en mieux! Découle de la lib standard Scala L'API de collection Scala se décline en version mutable ou immuable pour une même interface API à mi chemin, implémentation partiellement partagée source de bugs... Vavr ayant choisi de ne faire que de l'immuable, tout est grandement simplifié
  47. 47. Précédemment javaslang... Actuellement en 0.9.2 Changement de nom car pb de copyright, Java étant la marque d’Oracle. Le nom Vavr a été trouvé en regardant le reflet d’un sticker « javaslang ». En profite pour un "reset", pas mal de modif non rétrocompatible prévues pour la 1.0.0
  48. 48. Optional.map différent Transforme les retours null en empty()
  49. 49. Option/Try/Either/List/... map() retourne les nulls
  50. 50. Transformer null en None toujours possible flatMap(x -> Option.of(x)) Déjà, retourner les nulls n’empêche pas d’atteindre le même résultat qu’Optional si on le désire
  51. 51. map() retournant les nulls uniforme null != None associatif Mais au-delà, transformer les null en None serait étrange dans un Try ou un Either… Sans parler d’une List où null est une valeur valide ! D’ailleurs, dans une Map, avoir une clé avec une valeur nulle est différente de l’absence de valeur. Optional perd donc de l’information. Cette perte d’information est en fait dramatique : suivant l’ordre dans lequel on applique les map, Optional peut avoir différents résultats. En effet, nombreuses sont les méthodes pouvant retourner un résultat pour une valeur nulle. Or celles ci ne seront jamais appelée dans le cas d’Optional. Cet aspect, l’associativité, est une des lois des monades.
  52. 52. map() retournant les nulls Risque de NullPointerException ? On risque toujours des NPE, même avec Optional. Au sein d’un map on met du code. Et là le risque de NPE persiste. Null est bel et bien une valeur possible en Java, Optional ne pourra rien y changer. A propos, qui a tenté de retourner null dans le flatMap d’Optional ? Du coup le gain de transformer les nulls en empty est très faible, alors que les pertes sont énormes.
  53. 53. Retour au sujet Où en étions nous ? Pas simple de revenir sur un sujet après une distraction hein ? Vrai problème...
  54. 54. Quid d’une doc pour avoir un récap ? Une petite doc dans l’idée devrait bien faire le job non ? On n’a pas 200 règles, on pourrait facilement avoir un aperçu…
  55. 55. Une doc toujours à jour ? Seul hic, 99 % des docs ne sont pas à jour… Et le 1 % restant est souvent infâme de relents de procédures qualité ^^ Mais ce serait tellement bien...
  56. 56. Une doc interactive ? Encore mieux qu’à jour, quid d’une doc qu’on pourrait manipuler ? Ajouter des cas, voir ce qu’ils retournent… A la volée, comme ça, parce qu’on le veut ?
  57. 57. Informaticiens Protecteurs des Animaux On sait aussi teaser hein ? Vous pensez que c’est du rêve tout ça ? Vous voulez la réponse ?
  58. 58. Elements validés en vert, éléments en erreur en rouge, avec résultats effectifs affichés et ceux erronés barrés.
  59. 59. Spécifications par l’exemple L’idée des spécifications par l’exemple est qu’un exemple vaut beaucoup d’explications. C’est en discutant d’exemples concrets que l’on parvient à affiner. Il est donc important de matérialiser et mettre en conserver ces exemples.
  60. 60. Affichage via html ou markdown Java ou .Net L’html (ou le markdown) donne une liberté de présentation infinie fortement appréciable, que ce soit en termes de contenu (choix de la langue) ou présentation.
  61. 61. Communication possible de l’html ou du markdown Avec ou sans exécution du test On peut passer les fichiers bruts à n’importe qui. Là l’html a un avantage à mon sens : tout le monde a un navigateur a même de l’ouvrir, un outil pour visualiser du markdown est plus compliqué à avoir.
  62. 62. Je me suis occupé de la mise en forme… Bien sûr qu’on en passe l’html d’origine sans exécution du test, on ne sait pas ce qui valide ou pas. Ceci dit, cela ne devrait casser que dans des feature branchs, pas en prod ou develop. Au développeur de s’assurer qu’il passe la bonne version. On peut aussi directement accéder à ces fichiers, en tant que non codeurs, via un outil de navigation de repo (github…).
  63. 63. Instrumentation de l’affichage & Appels à la classe de test Évidemment ça ne marche pas tout seul ^^
  64. 64. <table concordion:execute="#result = validate(#name,#birthdate)"> <tr> <th concordion:set="#name">Nom</th> <th concordion:set="#birthdate">Date de naissance <br/>(yyyy/MM/dd)</th> <th concordion:assert- equals="#result">Résultat</th> </tr> <tr> <td>Dupont</td> <td>1999/03/21</td> <td>Person(Dupont, 18)</td> </tr> </table> Coté htlm, on ajoute des éléments qui ne perturbent pas l’affichage, indiquant : - ce qu’on capture - ce qu’on exécute - ce qu’on vérifie. Dans le cas d’un tableau, on définit une fois le comportement au niveau de l’header. Cela s’applique alors sur chaque ligne.
  65. 65. @RunWith(ConcordionRunner.class) public class PersonValidatorTest { public String validate( String name, String birthdate) { Either<String, String> strings = new PersonValidator() .validatePerson(name, birthdate) .map(Person::toString); return strings .getOrElse(() -> strings.getLeft()); } } La classe de test fait le lien entre les méthodes appelées depuis l’affichage et le code testé. Ici on « aplatit » les sorties pour les passer toutes en String.
  66. 66. Exécution avec les tests unitaires Embarqué avec le code Le coût d’exécution d’un concordion est minime. On peut les exécuter en même temps que les tests unitaires (ce qui est le comportement par défaut). Le fait que tout soit avec le code est génial. En effet, si on utilise un gestionnaire de sources alors on a l’historique des changements et on peut adapter les concordion dans une feature branch quand cela s’impose, sans impacter les autres branches.
  67. 67. Exemples hors tables possibles Également intéressant, mais plus limité en portée et plus coûteux en instrumentation.
  68. 68. Nombreuses extensions Import d’xls Captures d’écran ... Non content de décrire les spécifications, on peut aussi faire une « vraie doc ».
  69. 69. Documentation vivante ! Une documentation vivante : - s’adapte aux changements (captures actualisées) - dit si elle est périmée (tests qui plantent) - s’adapte aux différentes versions (est stockée par branche, avec le code)
  70. 70. Du (good) code Attention toutefois, la facilité d’instrumentation, via du code, peut parfois conduire à des usines à gaz, comme tout code. Toujours penser à limiter le scope, à refactorer, à découper si cela devient trop complexe. On est vite embarqué dans un cycle perfectionniste de l’affichage, mais ce n’est pas une fin en soi !
  71. 71. Jouons un peu avec les spécifications Souvent en ajoutant des exemples on découvre des choses...
  72. 72. Arrêt à la première erreur de validation...
  73. 73. Validation<E, T> <E> value type in the case of invalid <T> value type in the case of valid Très utile pour toutes les validations, notamment d’une IHM.
  74. 74. Validation<String, String> nomValidation( String nom) { return Option.of(nom) .filter(n -> !n.trim().isEmpty()) .toValidation("Le nom doit être renseigné avec des caractères alpha numériques") .flatMap(s -> checkName(s).toInvalid(s)); } On indique le type d’erreur à remonter, là on a pris String, pour l’exemple, mais dans la vraie vie ça peut être bien plus évolué. On transforme l’état stocké dans la monade, ici le nom validé.. On décide alors s l’état de la monade est valide ou non puis on fournit l’autre information pour faire la validation.
  75. 75. Validation<Seq<String>, Person> personValidation( String name, String birthdate) { return Validation .combine( nomValidation(name), ageValidation(birthdate)) .ap(Person::new); } A plus haut niveau, soit tout est valide et on construit une instance de ce qui est validé, soit on retourne les erreurs.
  76. 76. Retour à Concordion :)
  77. 77. Conversion en validation possible pour toutes les monades <E> Validation<E, T> toValid(E error) <U> Validation<T, U> toInvalid(U value) Du moins en vavr ;)
  78. 78. Validation via du code Si besoin de différentes validations suivant le contexte, easy :)
  79. 79. Validation<Seq<String>, Person> personValidation(..) Un problème là qq’un ?
  80. 80. Collections immuables ?
  81. 81. Et la mutabilité alors ?
  82. 82. Partage de Stream de Java 8 possible ? - Oui, mais le premier qui collecte plante le Stream pour tous les autres consommateurs! - Oh, ce partage est il thread safe? Plein de choses à avoir à l’esprit !
  83. 83. Vivre avec java.util.ArrayList<E>... Risque de changement dans la liste sans qu'on le sache... Que ce soit dans le code écrit dès à présent ou ... dans tout code écrit dans le futur!! Retour à la définition de code propre : Est ce le cas avec une list muable? Si oui, pouvez vous en être sûrs? Bien sûr, des palliatifs sont possibles: copies défensives, Collections.immutableList... Mettez vous toujours en oeuvre ces palliatifs ? A quel coût ? A propos, les éléments d'une immutableList peuvent ils être modifiés ?
  84. 84. Immuabilité == tranquillité Quand tout est immuable, pas d’inquiétude. On peut passer son objet à tout le monde, ils ne pourront pas faire de mal. Contexte multi threadé ou non. Plus besoin de programmation défensive, plus de bug de folies dus à un changement non prévu. Que du bonheur :)
  85. 85. Et les perfs alors ?
  86. 86. Persistent data structure Ensemble d’algorithmes pour rendre efficace les structures immuables, que ce soit en coût de garbage collection ou rapidité de modification/consultation.
  87. 87. List<Integer> list1 = List.of(1, 2, 3); // List(1, 2, 3) List<Integer> list2 = list1.tail().prepend(0); // List(0, 2, 3) On parle là de « structural sharing » : partage de données. On évite ainsi de créer de nouveaux tableaux et de faire pleins de nouvelles références à chaque action, voir même de dupliquer les objets référencés « par précaution »..
  88. 88. Très peu de garbage collection
  89. 89. SortedSet<Integer> xs = TreeSet.of(6, 1, 3, 2, 4, 7, 8); // TreeSet(1, 2, 3, 4, 6, 7, 8) SortedSet<Integer> ys = xs.add(5); // TreeSet(1, 2, 3, 4, 5, 6, 7, 8) La structure en arbre permet toujours le « structural sharing » mais en plus des mises à jour et parcours rapides. Lors des changements on change juste les « branches » affectées, le reste peut être réutilisé :)
  90. 90. Utilisation de Tree Accès en temps constant Performances ! Au delà du partage de structure, des algoritmes tels que le Red/Black Tree permet de s’assurer de l’accès à chaque donnée en 4 sauts maximum, soit un temps d’accès constant quelque soit la taille du Set… D’ailleurs ces techniques ont été repris dans pour les collections de java.util.concurrent depuis java 7. Il y a même des discussions pour passer la taille d’une ConcurrentHashMap à long : désormais int et ses 4 milliards peuvent être atteints sans problème.
  91. 91. Y a pas que les perfs ! Et l’IPA ? Au delà du partage de structure, des algoritmes tels que le Red/Black Tree permet de s’assurer de l’accès à chaque donnée en 4 sauts maximum, soit un temps d’accès constant quelque soit la taille du Set… D’ailleurs ces techniques ont été repris dans pour les collections de java.util.concurrent depuis java 7. Il y a même des discussions pour passer la taille d’une ConcurrentHashMap à long : désormais int et ses 4 milliards peuvent être atteints sans problème.
  92. 92. List<Integer> ints = List.of(1, 22); // List(1, 22) ints.head(); // 1 ints.headOption(); // Some(1) tail(); // List(22) ints.tailOption(); // Some(List(22)) En termes d’accesseurs, on peut aisément avoir le premier élément, head, et les suivants, tail.
  93. 93. ints; // List(1, 22) ints.map(i -> "Mon int est " + i); // List(Mon int est 1, Mon int est 22) ints.flatMap(i -> List.of(i)); // List(1, 22) Bien sûr, map et flatMap sont toujours là :)
  94. 94. ints; // toujours List(1, 22)! ints.fold(0, (acc, val) -> acc + val); // 23 ints.foldLeft("acc", (acc, val) -> acc + val); // acc122 ints.foldRight("acc", (val, acc) -> acc + val); // acc221 Fold permet de parcourir tous les éléments de la liste et d’accumuler le résultat au fur et à mesure FoldLeft parcours la liste en commençant par la gauche FoldRight parcours la liste en commençant par la droite Ces deux derniers folds permettent d’avoir un accumulateur de type différent que les éléments.
  95. 95. ints; // toujours List(1, 22)! ints.zipWithIndex(); // List((1, 0), (22, 1)) ints.intersperse(5); // List(1, 5, 22) ints.distinct(); // List(1, 22) ints.find(i -> i > 10); // Some(22) Avec les folds et les méthodes précédentes, on devine vite qu’on peut faire plein de choses, mais vavr a la gentillesse de nous proposer les plus courantes et/ou les plus pratiques ! De façon générale, rares sont les cas où vavr n’a pas de quoi grandement simplifier notre quotidien :)
  96. 96. Pas de .stream() et autres .collect()
  97. 97. Offre en fait bien plus ! Pattern matching, stream (collection infinies)... Vivement la 1.0 :)
  98. 98. That’s all folks… until next time !

×