Java Videotraining Werbung

Videotraining Spring 3 Boot Werbung

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 kennen

  • Varianten von Lambda-Ausdrücken unterscheiden und einsetzen können

  • Methoden- und Konstruktorreferenzen einsetzen können

  • grundlegender Inhalt und Aufbau des Pakets java.util.function kennen

  • interne und externe Iteration einsetzen können

Verwendete Datentypen in diesem Kapitel:

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

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 die List-Methode replaceAll(…​) 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. Wer sich für die Geschichte interessiert, findet unter http://www.javac.info/ die alten Entwürfe.