Vavr.io : la bibliothèque objet-fonctionnelle

Vavr (anciennement Javaslang) est une bibliothèque objet-fonctionnelle, destinée aux applications codées en Java 8 et plus. Elle permet de réduire le nombre de lignes de code et d'augmenter sa qualité. Elle fournit notamment des collections immuables et des structures de contrôle orientées fonctionnelles ce qui permet de réduire les effets de bord. Nous verrons dans cet article quelques possibilité offertes par cette bibliothèque.

Les collections immuables

Toutes les collections fournies dans Vavr sont immuables, ce qui permet d'éviter les effets de bord, en particuliers dans des applications multi-threadées.
Java permet, de base, l'utilisation de collections immuables, cependant si on essaye d'ajouter des éléments dedans, une exception est levée au runtime.

List<String> list = Collections.unmodifiableList(otherList);

list.add("quelque chose"); // lève une UnsupportedOperationException

Avec Vavr, lorsque l'on veut modifier une collection, celle-ci n'est pas modifiée mais une nouvelle instance est retournée.

Cela permet aussi de chaîner les appels. Exemple avec une Queue :

Queue<Integer> queue = Queue.of(1, 2, 3).enqueue(4).enqueue(5);
Chaînage d'insertions

Outre les collections classiques de Java, Vavr propose la structure de données Tuple, qui n'a pas d'équivalent en Java. Celle-ci permet de stocker un nombre fini (jusqu'a 8) d'éléments de types différents. Pour accéder à ces éléments, il faut utiliser, par exemple la méthode t._1 pour le 1er élément, t._2 pour le 2ème, et ainsi de suite. Exemple issu de la documentation :

Tuple2<String, Integer> java8 = Tuple.of("Java", 8); 

// "Java"
String s = java8._1; 

// 8
Integer i = java8._2;

 

Les value types

En plus des collections immuables, Vavr fournit quelques types immuables.

Try

Try est une interface qui permet de wrapper un calcul pouvant retourner une valeur ou lever une exception. Un objet Try peut être de type Success ou Failure.
Exemple d'utilisation :

Try.of(() -> maMethode()).getOrElse(other) // retourne other si maMethode lève une exception
Try

Lazy

Lazy est un wrapper qui représente une valeur calculée tardivement, c'est-à-dire lorsque l'on en a besoin. De plus, cela permet de mettre en cache la valeur calculée et de la retourner à chaque fois sans répéter le calcul. Par exemple :

Lazy<UUID> lazy = Lazy.of(UUID::randomUUID);
lazy.isEvaluated(); // = false
lazy.get();         // = 130e8900-a29f-11d4-a916-446655440200
lazy.isEvaluated(); // = true
lazy.get();         // =130e8900-a29f-11d4-a916-446655440200 (en mémoire)
Lazy

Either

Either représente une possible valeur de deux types différents. Either a deux implémentations : Left et Right.

Par convention, les erreurs sont retournées à gauche et le résultat à droite.

Exemple : une méthode calcul() dont le résultat est soit un entier (en cas de succès) soit un message d'erreur (en cas d'échec).

Either<String,Integer> resultat= maFonction().right().map(i -> i * 2).toEither();
Either

Si le résultat de calcul() est Right(1), la valeur de resultat sera Right(2).
Si le résultat de calcul() est Left("erreur"), la valeur de resultat sera Left("erreur").

Future

Ce type représente le résultat d'un calcul qui devient disponible dans le futur. Chaque opération est non-bloquante. Un objet Future a deux états : pending (en attente) et completed.
On peut mettre des callbacks sur un objet Future. Ces actions sont réalisées dès que le Future atteint l'état completed. Une action ajoutée sur un Future completed est immédiatement exécutée. Pour plus d'information, voir la documentation.

 En plus des types sus-cités, Vavr propose les types suivants : Option, qui se rapproche des Optional de Java 8 et Validation.
 Les Functions

Java 8 fournit seulement une classe Function qui prend un paramètre et une classe BiFunction qui en prend deux.
Vavr fournit des classes permettant de créer des fonctions jusqu'à 8 paramètres : Function0, Function1, Function2, Function3...
Exemple :

// somme.apply(1, 2, 3) = 6
Function3<Integer, Integer, Integer, Integer> sum = (a, b, c) -> a + b + c;
Fonction acceptant 3 paramètres

Les interfaces fonctionnelles de Vavr fournissent, en outre, les possibilités suivantes :

Pattern Matching

Le concept de pattern matching est un concept natif à la plupart des langages fonctionnels qui n'est pas inclus de base dans Java, ce qui oblige souvent à utiliser une longue suite de if  ou un switch pouvant comprend un nombre de lignes important.
Le pattern matching de Vavr permet d'écrire du code plus concis et plus lisible.

Exemple d'utilisation :

Match(arg).of(
    Case($(isIn("-h", "--help")), o -> run(this::displayHelp)),
    Case($(isIn("-v", "--version")), o -> run(this::displayVersion)),
    Case($(), o -> run(() -> {
        throw new IllegalArgumentException(arg);
    }))
);
Exemple de pattern matching (source : documentation officielle)

Pour plus d'informations sur cette bibliothèque, consultez la documentation.

Sources

Le site de Vavr
Le manuel d'utilisateur
La Javadoc

Un commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *