1. Lambda-Ausdrücke und funktionale Programmierung
Lambda-Ausdrücke sind Abbildungen und die Basis der funktionalen Programmierung. Bei der funktionalen Programmierung stehen Funktionen im Mittelpunkt, also im besten Fall pure Ausdrücke ohne Seiteneffekte. In Java kann man keine Funktionen — also Methoden — direkt an andere Methoden übergeben oder zurückgegeben, also ist der Trick, diese Methoden in eine Klasse zu setzen und als Objekt zu übergeben. Und wie implementiert man schnell eine Klasse mit einer gewissen Schnittstelle? Mit Lambda-Ausdrücken! Lambda-Ausdrücke sind Implementierungen von funktionalen Schnittstellen — also Schnittstellen mit genau einer abstrakten Methode — und eine Alternative und Abkürzung zu Klassen, die Schnittstellen implementieren. So lässt sich sehr einfach Programmcode ausdrücken und an andere Methoden übergeben.
In den folgenden Aufgaben geht es um die unterschiedlichen Schreibweisen von Lambda-Ausdrücken und einigen wichtigen funktionalen Schnittstellen, die an verschiedenen Stellen in Java-Bibliotheken immer wieder vorkommen.
Voraussetzungen
Generics lesen können
funktionale Schnittstellen und Zusammenhang mit Lambda-Ausdrücken verstehen
Annotation
@FunctionalInterface
kennenVarianten von Lambda-Ausdrücken unterscheiden und einsetzen können
Methoden- und Konstruktorreferenzen einsetzen können
grundlegender Inhalt und Aufbau des Pakets
java.util.function
kenneninterne und externe Iteration einsetzen können
Verwendete Datentypen in diesem Kapitel:
Noch mehr Aufgaben findest du im Buch: ›Captain CiaoCiao erobert Java: Das Trainingsbuch für besseres Java. 300 Java-Workshops, Aufgaben und Übungen mit kommentierten Lösungen‹
1.1. Lambda-Ausdrücke
Dank der kompakten Schreibweise von Lambda-Ausdrücken rückt die Modellierung von Abbildungen stärker in den Fokus. Natürlich ist eine Implementierung von Schnittstellen seit Java 1.0 möglich, doch der Codeumfang ist mit regulären Klassen zu groß, und die Modellierung von Bibliotheken, die »Funktionen« annehmen und liefern, ist auf wenig Interesse gestoßen. Die ersten Prototypen erschienen um 2006, es dauerte aber bis Java 8 (März 2014), bis Lambda-Ausdrücken in die Sprache einzogen.[1]
1.1.1. Lambda-Ausdrücke für funktionale Schnittstellen schreiben ⭐
In der Java-Bibliothek gibt es eine große Menge an Schnittstellen, insbesondere an funktionalen Schnittstellen, also Schnittstellen mit nur einer abstrakten Methode. Diese lassen sich gut mit Lambda-Ausdrücken realisieren.
Aufgabe:
Weise den folgenden Variablen jeweils einen gültigen Lambda-Ausdruck zu. Die Variablentypen sind funktionale Schnittstellen.
/* interface Runnable { void run(); } interface ActionListener { void actionPerformed(ActionEvent e); } interface Supplier<T> { T get(); } interface Consumer<T> { void accept(T t); } interface Comparator<T> { int compare(T o1, T o2); } */ Runnable runnable = ... ActionListener listener = ... Supplier<String> supplier = ... Consumer<Point> consumer = ... Comparator<Rectangle> comparator = ...
Die Lambda-Ausdrücke müssen nur gültig compilierbar sein, die Logik muss keinen Sinn ergeben.
Die Deklarationen der funktionalen Schnittstellen befinden sich zum besseren Verständnis im Kommentar, erfüllen aber sonst keine weitere Funktion.
Experimentiere mit verschiedenen kompakten Schreibweisen.
1.1.2. Lambda-Ausdrücke entwickeln ⭐
Wir haben gesehen, dass im Paket java.util.function
viele funktionale Schnittstellen liegen. Viele der Schnittstellen enthalten aber mehrere Methoden, Default-Methoden und statische Methoden.
Aufgabe:
Suche aus der Javadoc die abstrakte Methode der folgenden Typen, und implementiere sie durch einen Lambda-Ausdruck, der keinen Sinn ergeben muss:
DoubleSupplier ds = ... LongToDoubleFunction ltdf = ... UnaryOperator<String> up = ...
1.2. Ausgewählte funktionale Schnittstellen
Die Typen Predicate
, UnaryOperator
und Consumer
sind funktionale Schnittstellen, die oft als Parametertypen vorkommen, etwa bei der Collection-API, wenn es zum Beispiel darum geht, Elemente nach einem gewissen Kriterium zu löschen oder zu transformieren.
1.2.1. Einträge löschen, Kommentare entfernen, in CSV konvertieren ⭐
Die Aufgabe konzentriert sich auf drei Methoden, die Listen haben:
default boolean removeIf(Predicate<? super E> filter)
(Collection
)default void replaceAll(UnaryOperator<E> operator)
(List
)default void forEach(Consumer<? super T> action)
(Iterable
)
Die drei Methoden führen eine interne Iteration durch, sodass nicht wir durch die Liste iterieren müssen. Wir müssen nur die Operation angeben, die auf jedem Element angewendet wird.
Bonny Brain und die Crew planen eine Städtetour. Jede Stadt ist durch zwei Informationen gegeben: Name und Einwohnerzahl:
record City( String name, int population ) { }
Die Ziele der Städtereise stehen in einer ArrayList
.
List<City> cityTour = new ArrayList<>();
City g = new City( "Gotham (cathedral)", 8_000_000 );
City m = new City( "Metropolis (pleasure garden)", 1_600_000 );
City h = new City( "Hogsmeade (Shopping Street)", 1_124 );
Collections.addAll( cityTour, g, m, h );
Aufgabe:
Nutze
removeIf(…)
, sodass am Ende nur die größeren Städte mit mehr als 10.000 Einwohner bleiben.Die Städtenamen enthalten in runden Klammern Kommentare. Wenn Kommentare enthalten sind, stehen sie immer am Ende des Strings. Lösche die Kommentare, und greife zum Ersetzen der
City
-Objekte auf dieList
-MethodereplaceAll(…)
zurück.Nutze
forEach(…)
, sodass am Ende die Daten der Städte im CSV-Format (kommaseparierte Ausgabe) auf dem Bildschirm erscheinen.
Beispiel:
Aus der oberen gegebenen Liste folgt die Ausgabe:
Gotham,8000000 Metropolis,1600000
1.3. Funktionale Bibliotheken
Für Java gibt es eine riesige Anzahl von Bibliotheken, und das ist auch ein Grund, warum Java so populär ist. Einige Ergänzungen übersetzen funktionale Prinzipien in die Java-Welt.
1.3.1. VAVR ⭐⭐⭐
Studiere https://www.vavr.io/vavr-docs/#_values.
Noch mehr Aufgaben findest du im Buch: ›Captain CiaoCiao erobert Java: Das Trainingsbuch für besseres Java. 300 Java-Workshops, Aufgaben und Übungen mit kommentierten Lösungen‹