Einführung in funktionelles Java

Einführung in funktionales Java

1. Überblick

In diesem Tutorial geben wir einen kurzen Überblick über dieFunctional Java-Bibliothek sowie einige Beispiele.

2. Die funktionale Java-Bibliothek

Die Functional Java-Bibliothek ist eine Open Source-Bibliothek, die die funktionale Programmierung in Java erleichtern soll. Die Bibliothek bietet viele grundlegende und erweiterte Programmierabstraktionen, die üblicherweise inFunctional Programming verwendet werden.

Ein Großteil der Funktionalität der Bibliothek dreht sich um dieF-Schnittstelle. This F interface models a function that takes an input of type A and returns an output of type B. All dies basiert auf dem Java-eigenen Typsystem.

3. Maven-Abhängigkeiten

Zuerst müssen wir die erforderlichendependencies zu unsererpom.xml-Datei hinzufügen:


    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. Eine Funktion definieren

Beginnen wir mit der Erstellung einer Funktion, die wir später in unseren Beispielen verwenden können.

Ohne Functional Java würde eine grundlegende Multiplikationsmethode ungefähr so ​​aussehen:

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

Mit der Functional Java-Bibliothek können wir diese Funktionalität etwas eleganter definieren:

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

Oben sehen wir ein Beispiel für dieF-Schnittstelle, dieInteger als Eingabe verwendet undInteger mal zwei als Ausgabe zurückgibt.

Hier ist ein weiteres Beispiel für eine Grundfunktion, dieInteger als Eingabe verwendet, in diesem Fall jedochBoolean zurückgibt, um anzuzeigen, ob die Eingabe gerade oder ungerade war:

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

5. Anwenden einer Funktion

Nachdem wir unsere Funktionen eingerichtet haben, wenden wir sie auf einen Datensatz an.

Die Functional Java-Bibliothek bietet die üblichen Typen zum Verwalten von Daten wie Listen, Mengen, Arrays und Karten. The key thing to realize is that these data types are immutable.

Zusätzlich stellt die Bibliothek bei Bedarfconvenience functions to convert to and from standard Java Collections classes zur Verfügung.

Im folgenden Beispiel definieren wir eine Liste von Ganzzahlen und wenden unseretimesTwo-Funktion darauf an. Wir rufen auchmap mit einer Inline-Definition derselben Funktion auf. Natürlich erwarten wir die gleichen Ergebnisse:

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));
}

Wie wir sehen können, gibtmap eine Liste derselben Größe zurück, wobei der Wert jedes Elements der Wert der Eingabeliste mit der angewendeten Funktion ist. Die Eingabeliste selbst ändert sich nicht.

Hier ist ein ähnliches Beispiel mit unsererisEven-Funktion:

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. Die Reihenfolge, in der wir unseremap-Funktionen aufrufen, ändert unsere resultierende Ausgabe:

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));
}

Die Ausgabe der obigen Listen ist:

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

6. Filtern mit einer Funktion

Eine andere häufig verwendete Operation in der funktionalen Programmierung isttake an input and filter out data based on some criteria. Und wie Sie wahrscheinlich bereits vermutet haben, werden diese Filterkriterien in Form einer Funktion bereitgestellt. Diese Funktion muss einen Booleschen Wert zurückgeben, um anzugeben, ob die Daten in der Ausgabe enthalten sein müssen.

Verwenden wir nun unsereisEven-Funktion, um die ungeraden Zahlen aus einem Eingabearray mit derfilter-Methode herauszufiltern:

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));
}

Eine interessante Beobachtung ist, dass wir in diesem Beispiel einArray anstelle einesList verwendet haben, wie wir es in den vorherigen Beispielen verwendet haben, und dass unsere Funktion gut funktioniert hat. 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.

In diesem Beispiel haben wir auch unsere eigeneisEven-Funktion verwendet, aber dieInteger-Klasse von Functional Java verfügt auch über Standardfunktionen fürbasic numerical comparisons.

7. Anwenden der Booleschen Logik mithilfe einer Funktion

In der funktionalen Programmierung verwenden wir häufig Logik wie „Tun Sie dies nur, wenn alle Elemente eine Bedingung erfüllen“ oder „Tun Sie dies nur, wenn mindestens ein Element eine Bedingung erfüllt“.

Die Functional Java-Bibliothek bietet Verknüpfungen für diese Logik mit den Methodenexists undforall:

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)));
}

Im obigen Beispiel haben wir ein Array von Zeichenfolgen als Eingabe verwendet. Durch Aufrufen der FunktionfromString werden alle Zeichenfolgen aus dem Array in eine Liste von Zeichen konvertiert. Auf jede dieser Listen haben wirforall(Characters.isLowerCase) angewendet.

Wie Sie wahrscheinlich erraten haben, istCharacters.isLowerCase eine Funktion, die true zurückgibt, wenn ein Zeichen in Kleinbuchstaben geschrieben ist. Wenn Sie alsoforall(Characters.isLowerCase) auf eine Liste von Zeichen anwenden, wirdtrue nur zurückgegeben, wenn die gesamte Liste aus Kleinbuchstaben besteht, was wiederum anzeigt, dass die ursprüngliche Zeichenfolge nur aus Kleinbuchstaben bestand.

In den ersten beiden Tests haben wirexists verwendet, weil wir nur wissen wollten, ob mindestens eine Zeichenfolge in Kleinbuchstaben geschrieben ist. Der dritte Test verwendeteforall, um zu überprüfen, ob alle Zeichenfolgen in Kleinbuchstaben geschrieben waren.

8. Umgang mit optionalen Werten mit einer Funktion

Die Behandlung optionaler Werte im Code erfordert normalerweise== null oderisNotBlank Prüfungen. Java 8 bietet jetzt dieOptional-Klasse, um diese Prüfungen eleganter zu handhaben, und die Functional Java-Bibliothek bietet ein ähnliches Konstrukt, um fehlende Daten über dieOption-Klasse ordnungsgemäß zu behandeln:

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. Reduzieren eines Satzes mithilfe einer Funktion

Schließlich werden wir uns die Funktionalität zum Reduzieren eines Satzes ansehen. "Reduzieren einer Menge" ist eine originelle Art zu sagen, wie man "eine Menge zu einem Wert zusammenfasst".

The Functional Java library refers to this functionality as folding.

Es muss eine Funktion angegeben werden, die angibt, was das Falten des Elements bedeutet. Ein Beispiel hierfür ist die FunktionIntegers.add, mit der die Ganzzahlen in einem Array oder einer Liste angezeigt werden, die hinzugefügt werden müssen.

Abhängig davon, was die Funktion beim Falzen ausführt, kann das Ergebnis unterschiedlich sein, je nachdem, ob Sie mit dem Falzen von rechts oder von links beginnen. Aus diesem Grund bietet die Functional Java-Bibliothek beide Versionen:

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);
}

Das erstefoldLeft addiert einfach alle ganzen Zahlen. Während die zweite zuerst einen Filter anwendet und dann die verbleibenden ganzen Zahlen addiert.

10. Fazit

Dieser Artikel ist nur eine kurze Einführung in die Functional Java-Bibliothek.

Wie immer ist der vollständige Quellcode des Artikelsover on GitHub verfügbar.