Introduction à Java fonctionnel

Introduction à Java fonctionnel

1. Vue d'ensemble

Dans ce didacticiel, nous allons fournir un aperçu rapide de la bibliothèqueFunctional Java avec quelques exemples.

2. La bibliothèque fonctionnelle Java

La bibliothèque Java fonctionnelle est une bibliothèque open source destinée à faciliter la programmation fonctionnelle en Java. La bibliothèque fournit de nombreuses abstractions de programmation de base et avancées couramment utilisées dansFunctional Programming.

Une grande partie des fonctionnalités de la bibliothèque tourne autour de l’interfaceF. This F interface models a function that takes an input of type A and returns an output of type B. Tout cela est construit sur le système de type de Java.

3. Dépendances Maven

Tout d'abord, nous devons ajouter lesdependencies requis à notre fichierpom.xml:


    org.functionaljava
    functionaljava
    4.8.1


    org.functionaljava
    functionaljava-java8
    4.8.1


    org.functionaljava
    functionaljava-quickcheck
    4.8.1


    org.functionaljava
    functionaljava-java-core
    4.8.1

4. Définir une fonction

Commençons par créer une fonction que nous pourrons utiliser dans nos exemples plus tard.

Sans Java fonctionnel, une méthode de multiplication de base ressemblerait à quelque chose comme:

public static final Integer timesTwoRegular(Integer i) {
    return i * 2;
}

En utilisant la bibliothèque Java fonctionnelle, nous pouvons définir cette fonctionnalité un peu plus élégamment:

public static final F timesTwo = i -> i * 2;

Ci-dessus, nous voyons un exemple de l'interfaceF qui prend unInteger en entrée et renvoie ceInteger fois deux en sortie.

Voici un autre exemple de fonction de base qui prend unInteger comme entrée, mais dans ce cas, renvoie unBoolean pour indiquer si l'entrée était paire ou impaire:

public static final F isEven = i -> i % 2 == 0;

5. Appliquer une fonction

Maintenant que nos fonctions sont en place, appliquons-les à un ensemble de données.

La bibliothèque Java fonctionnelle fournit l'ensemble des types habituels pour la gestion de données telles que des listes, des ensembles, des tableaux et des cartes. The key thing to realize is that these data types are immutable.

De plus, la bibliothèque fournit desconvenience functions to convert to and from standard Java Collections classes si nécessaire.

Dans l'exemple ci-dessous, nous allons définir une liste d'entiers et y appliquer notre fonctiontimesTwo. Nous appellerons égalementmap en utilisant une définition en ligne de la même fonction. Bien entendu, nous nous attendons à ce que les résultats soient les mêmes:

public void multiplyNumbers_givenIntList_returnTrue() {
    List fList = List.list(1, 2, 3, 4);
    List fList1 = fList.map(timesTwo);
    List fList2 = fList.map(i -> i * 2);

    assertTrue(fList1.equals(fList2));
}

Comme nous pouvons le voir,map renvoie une liste de même taille où la valeur de chaque élément est la valeur de la liste d’entrée avec la fonction appliquée. La liste de saisie elle-même ne change pas.

Voici un exemple similaire utilisant notre fonctionisEven:

public void calculateEvenNumbers_givenIntList_returnTrue() {
    List fList = List.list(3, 4, 5, 6);
    List evenList = fList.map(isEven);
    List evenListTrueResult = List.list(false, true, false, true);

    assertTrue(evenList.equals(evenListTrueResult));
}

Since the map method returns a list, we can apply another function to its output. L'ordre dans lequel nous invoquons nos fonctionsmap modifie notre sortie résultante:

public void applyMultipleFunctions_givenIntList_returnFalse() {
    List fList = List.list(1, 2, 3, 4);
    List fList1 = fList.map(timesTwo).map(plusOne);
    List fList2 = fList.map(plusOne).map(timesTwo);

    assertFalse(fList1.equals(fList2));
}

La sortie des listes ci-dessus sera:

List(3,5,7,9)
List(4,6,8,10)

6. Filtrage à l'aide d'une fonction

Une autre opération fréquemment utilisée dans la programmation fonctionnelle est detake an input and filter out data based on some criteria. Et comme vous l’avez probablement déjà deviné, ces critères de filtrage sont fournis sous la forme d’une fonction. Cette fonction devra renvoyer un booléen pour indiquer si les données doivent ou non être incluses dans la sortie.

Maintenant, utilisons notre fonctionisEven pour filtrer les nombres impairs d'un tableau d'entrée en utilisant la méthodefilter:

public void filterList_givenIntList_returnResult() {
    Array array = Array.array(3, 4, 5, 6);
    Array filteredArray = array.filter(isEven);
    Array result = Array.array(4, 6);

    assertTrue(filteredArray.equals(result));
}

Une observation intéressante est que dans cet exemple, nous avons utilisé unArray au lieu d'unList comme nous l'avons utilisé dans les exemples précédents, et notre fonction a bien fonctionné. Because of the way functions are abstracted and executed, they do not need to be aware of what method was used to collect the input and output.

Dans cet exemple, nous avons également utilisé notre propre fonctionisEven, mais la propre classeInteger de Functional Java a également des fonctions standard pourbasic numerical comparisons.

7. Application d'une logique booléenne à l'aide d'une fonction

Dans la programmation fonctionnelle, nous utilisons fréquemment une logique telle que «ne le faites que si tous les éléments satisfont à une condition», ou «ne le font que si au moins un élément remplit une condition».

La bibliothèque Functional Java nous fournit des raccourcis pour cette logique via les méthodesexists etforall:

public void checkForLowerCase_givenStringArray_returnResult() {
    Array array = Array.array("Welcome", "To", "example");
    assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));

    Array array2 = Array.array("Welcome", "To", "example");
    assertFalse(array2.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));

    assertFalse(array.forall(s -> List.fromString(s).forall(Characters.isLowerCase)));
}

Dans l'exemple ci-dessus, nous avons utilisé un tableau de chaînes comme entrée. L'appel de la fonctionfromString convertira chacune des chaînes du tableau en une liste de caractères. À chacune de ces listes, nous avons appliquéforall(Characters.isLowerCase).

Comme vous l'avez probablement deviné,Characters.isLowerCase est une fonction qui renvoie true si un caractère est en minuscules. Ainsi, appliquerforall(Characters.isLowerCase) à une liste de caractères ne renverratrue que si la liste entière se compose de caractères minuscules, ce qui indique alors que la chaîne d'origine était entièrement en minuscules.

Dans les deux premiers tests, nous avons utiliséexists car nous voulions seulement savoir si au moins une chaîne était en minuscule. Le troisième test a utiliséforall pour vérifier si toutes les chaînes étaient en minuscules.

8. Gestion des valeurs facultatives avec une fonction

La gestion des valeurs facultatives dans le code nécessite généralement des vérifications== null ouisNotBlank. Java 8 fournit désormais la classeOptional pour gérer ces vérifications avec plus d'élégance, et la bibliothèque Functional Java propose une construction similaire pour gérer les données manquantes avec élégance via sa classeOption:

public void checkOptions_givenOptions_returnResult() {
    Option n1 = Option.some(1);
    Option n2 = Option.some(2);
    Option n3 = Option.none();

    F> function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none();

    Option result1 = n1.bind(function);
    Option result2 = n2.bind(function);
    Option result3 = n3.bind(function);

    assertEquals(Option.none(), result1);
    assertEquals(Option.some(102), result2);
    assertEquals(Option.none(), result3);
}

9. Réduction d'un ensemble à l'aide d'une fonction

Enfin, nous examinerons les fonctionnalités permettant de réduire un ensemble. "Réduire un ensemble" est une façon élégante de dire "le transformer en une seule valeur".

The Functional Java library refers to this functionality as folding.

Une fonction doit être spécifiée pour indiquer ce que signifie replier l'élément. Un exemple de ceci est la fonctionIntegers.add pour montrer que les entiers dans un tableau ou une liste doivent être ajoutés.

En fonction de l'action de la fonction lors du pliage, le résultat peut être différent selon que vous commencez à le faire à droite ou à gauche. C’est pourquoi la bibliothèque Functional Java fournit les deux versions:

public void foldLeft_givenArray_returnResult() {
    Array intArray = Array.array(17, 44, 67, 2, 22, 80, 1, 27);

    int sumAll = intArray.foldLeft(Integers.add, 0);
    assertEquals(260, sumAll);

    int sumEven = intArray.filter(isEven).foldLeft(Integers.add, 0);
    assertEquals(148, sumEven);
}

Le premierfoldLeft ajoute simplement tous les entiers. Alors que le second appliquera d’abord un filtre puis ajoutera les entiers restants.

10. Conclusion

Cet article n’est qu’une brève introduction à la bibliothèque Java fonctionnelle.

Comme toujours, le code source complet de l'article est disponibleover on GitHub.