Was sind Java 8 Functional Interfaces?

Java 8 hat eine besonders interessante Funktionalität mitgebracht: Lambdas. Ein Lambda ist eine anonyme Funktion und kann etwa als Parameter einer Methode über- oder von einer Methode zurückgegeben werden.

Vor Java 8 hat man üblicherweise eine Klasse erstellt wenn man irgendein Stück Code verwenden wollte. Dies ist aber nicht immer nötig und generiert unnötigen Boilerplate Code.

Zu Lambdas komme ich aber ein anderes Mal. Jetzt geht es um die Functional Interfaces.

Kurze Übersicht

Functional Interfaces:

  1. Consumer = Akzeptiert ein Argument und gibt nichts zurück
  2. BiConsumer = Akzeptiert zwei Argumente, etwa von einer Hashmap, und gibt nichts zurück
  3. Supplier = Will kein Argument, aber gibt eines zurück
  4. Predicate = Gibt true oder false zurück
  5. Operator = Will den gleichen Typ als Input und Output

Wie sehen Functional Interfaces aus?

Ein Functional Interface hat üblicherweise eine @FunctionalInterface Annotation. Der Trick bei einem Functional Interface ist, dass diese eine einige abstrakte Methode besitzt. Die Implementation dieser Methode kann dann als Lambda geschehen.

Die neuen „Default“ Methoden, die auch mit Java 8 eingeführt wurden, sind übrigens nicht abstract und zählen daher auch nicht zu den Functional Interfaces.

Das Consumer Interface

Das Interface, was wohl am meisten verwendet wird, ist das Consumer Interface. Ein Consumer akzeptiert ein Argument und gibt nichts zurück. Mit dem Argument wird irgendetwas angestellt.

Zum Beispiel können wir eine Liste von Namen erstellen und dann jeden mit „Hallo“ begrüssen. Die Liste kann ganz einfach mit „Arrays.asList()“ gemacht werden (achtung: Das Arrays hat ein grosses ‚A‘).

List<String> namen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.forEach(name -> System.out.println("Hallo " + name");

Bei der Methode forEach sehen wir schön, dass ein Consumer benötigt wird, welcher einen Parameter braucht, in diesem Fall einen String.

funcint1.PNG

Output:
Hallo Hans
Hallo Wurst
Hallo Gerald

Wenn wir die Klasse „java.util.function.Consumer“ öffnen sieht man am Anfang die Annotation @FunctionalInterface. Die abstrakte Methode ist in diesem Fall „accept“, da wir aber ein obercooles Lambda mit dem -> Pfeil verwendet haben, müssen wir diesen Namen gar nicht kennen.

funcint2.PNG

PS: Die Angabe zu „references | implementation“ ist übrigens vom Code Mining, das gehört nicht zu der Klasse, siehe Code Mining in Eclipse Photon

Das BiConsumer Interface

Ein BiConsumer ist ähnlich wie der Consumer, nur dass er zwei Argumente annimmt, etwa von einer HashMap. Wieder kann man bei der forEach Methode schauen, was benötigt wird, und sieht, dass die Methode ein BiConsumer benötigt.

funcint3.PNG

Map<String, Integer> personen = new HashMap<>();
personen.put("Hans", 20);
personen.put("Wurst", 22);
personen.put("Gerald", 34);
personen.forEach((name, alter) -> System.out.println(name + 
" ist " + alter + " Jahre alt"));

Output:
Wurst ist 22 Jahre alt
Gerald ist 34 Jahre alt
Hans ist 20 Jahre alt

Das Supplier Interface

Der Supplier ist das Gegenteil vom Consumer und absolut selbstlos. Er will überhaupt keinen Parameter, liefert aber dennoch einen Wert zurück.

Das Interface sieht so aus:

funcint4.PNG

Es braucht in der Methode get() keinen Parameter, gibt aber irgendetwas zurück.

Das einfachste Beispiel erstellt einfach einen Supplier, der irgendetwas zurückgibt, etwa in einem Lambda. Nicht vergessen, beim Supplier den Typ mit eckigen Klammern mitzugeben.

Supplier<String> halloSupplier = () -> new String("Hallo");
String halloString = halloSupplier.get();

Das Predicate Interface

Ein Predicate ist eine Funktion, die Inputwerte erhält und nach einer gewissen Logik ein true oder false zurückgeben, also einen Boolean.

Zum Beispiel kann man prüfen, ob ein Name mit „W“ anfängt.

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
List<String> namenMitW = namen.stream()
.filter(name -> name.startsWith("W")).collect(Collectors.toList());
namenMitW.forEach(name -> System.out.println(name));

Output: 
Wurst

Das Operator Interface

Das Operator Interface ist eine spezielle Funktion, die den gleichen Typ als Input wie auch als Output hat.

Die Methode „replaceAll“ etwa möchte einen UnaryOperator haben.

funcint5.PNG

Was übrigens auch neu ist, ist der Zugriff auf bestimmte Methoden mit dem Doppelpunkt, wie im Beispiel mit String::toUpperCase. (Wenn man in Eclipse „String::“ eingibt und dann CTRL + Leertaste drückt kommt eine Liste von Methoden und man kann einfach eine auswählen)

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.replaceAll(String::toUpperCase);

Output: 
HANS
WURST
GERALD

Echt genial und gar nicht mal so kompliziert, wenn man mal ein paar einfache Beispiele gesehen hat, oder?

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s