Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien zum Buch
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichen und Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
9 Ausnahmen müssen sein
10 Geschachtelte Typen
11 Besondere Typen der Java SE
12 Generics<T>
13 Lambda-Ausdrücke und funktionale Programmierung
14 Architektur, Design und angewandte Objektorientierung
15 Java Platform Module System
16 Die Klassenbibliothek
17 Einführung in die nebenläufige Programmierung
18 Einführung in Datenstrukturen und Algorithmen
19 Einführung in grafische Oberflächen
20 Einführung in Dateien und Datenströme
21 Einführung ins Datenbankmanagement mit JDBC
22 Bits und Bytes, Mathematisches und Geld
23 Testen mit JUnit
24 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 13 Lambda-Ausdrücke und funktionale Programmierung
Pfeil 13.1 Funktionale Schnittstellen und Lambda-Ausdrücke
Pfeil 13.1.1 Klassen implementieren Schnittstellen
Pfeil 13.1.2 Lambda-Ausdrücke implementieren Schnittstellen
Pfeil 13.1.3 Funktionale Schnittstellen
Pfeil 13.1.4 Der Typ eines Lambda-Ausdrucks ergibt sich durch den Zieltyp
Pfeil 13.1.5 Annotation @FunctionalInterface
Pfeil 13.1.6 Syntax für Lambda-Ausdrücke
Pfeil 13.1.7 Die Umgebung der Lambda-Ausdrücke und Variablenzugriffe
Pfeil 13.1.8 Ausnahmen in Lambda-Ausdrücken
Pfeil 13.1.9 Klassen mit einer abstrakten Methode als funktionale Schnittstelle? *
Pfeil 13.2 Methodenreferenz
Pfeil 13.2.1 Motivation
Pfeil 13.2.2 Methodenreferenzen mit ::
Pfeil 13.2.3 Varianten von Methodenreferenzen
Pfeil 13.3 Konstruktorreferenz
Pfeil 13.3.1 Parameterlose und parametrisierte Konstruktoren
Pfeil 13.3.2 Nützliche vordefinierte Schnittstellen für Konstruktorreferenzen
Pfeil 13.4 Funktionale Programmierung
Pfeil 13.4.1 Code = Daten
Pfeil 13.4.2 Programmierparadigmen: imperativ oder deklarativ
Pfeil 13.4.3 Das Wesen der funktionalen Programmierung
Pfeil 13.4.4 Funktionale Programmierung und funktionale Programmiersprachen
Pfeil 13.4.5 Funktionen höherer Ordnung am Beispiel von Comparator
Pfeil 13.4.6 Lambda-Ausdrücke als Abbildungen bzw. Funktionen betrachten
Pfeil 13.5 Funktionale Schnittstellen aus dem java.util.function-Paket
Pfeil 13.5.1 Blöcke mit Code und die funktionale Schnittstelle Consumer
Pfeil 13.5.2 Supplier
Pfeil 13.5.3 Prädikate und java.util.function.Predicate
Pfeil 13.5.4 Funktionen über die funktionale Schnittstelle java.util.function.Function
Pfeil 13.5.5 Ein bisschen Bi …
Pfeil 13.5.6 Funktionale Schnittstellen mit Primitiven
Pfeil 13.6 Optional ist keine Nullnummer
Pfeil 13.6.1 Einsatz von null
Pfeil 13.6.2 Der Optional-Typ
Pfeil 13.6.3 Erst mal funktional mit Optional
Pfeil 13.6.4 Primitiv-Optionales mit speziellen Optional*-Klassen
Pfeil 13.7 Was ist jetzt so funktional?
Pfeil 13.8 Zum Weiterlesen
 

Zum Seitenanfang

13.2    Methodenreferenz Zur vorigen ÜberschriftZur nächsten Überschrift

Lambda-Ausdrücke sind schon recht kurz, aber für gewisse Aufrufe geht es noch ein bisschen kürzer.

 

Zum Seitenanfang

13.2.1    Motivation Zur vorigen ÜberschriftZur nächsten Überschrift

Je größer Softwaresysteme werden, desto wichtiger werden Dinge wie Klarheit, Wiederverwendbarkeit und Dokumentation. Wir haben für unseren String-Comparator eine Implementierung geschrieben, anfangs über eine innere Klasse, später über einen Lambda-Ausdruck. In jedem Fall haben wir Code geschrieben. Doch was wäre, wenn eine Utility-Klasse schon eine Implementierung mitbringen würde? Dann könnte der Lambda-Ausdruck natürlich an die vorhandene Implementierung delegieren, und wir sparen Code. Schauen wir uns das an einem Beispiel an:

Listing 13.10     src/main/java/com/tutego/insel/lambda/TrimCompareWithDelegation.java, Ausschnitt

class StringUtils {

public static int trimCompare( String s1, String s2 ) {

return s1.trim().compareTo( s2.trim() );

}

}

public class TrimCompareWithDelegation {

public static void main( String[] args ) {

String[] words = { "A", "B", "a" };

Arrays.sort( words,

(String s1, String s2) -> StringUtils.trimCompare(s1, s2) );

System.out.println( Arrays.toString( words ) );

}

}
 

Zum Seitenanfang

13.2.2    Methodenreferenzen mit :: Zur vorigen ÜberschriftZur nächsten Überschrift

Auffällig im Beispiel ist, dass die referenzierte Methode int trimCompare(String, String) von den Parametertypen und vom Rückgabetyp genauso wie int compare(String, String) ist und – wenn wir den Methodennamen weglassen – wie die Methode im Comparator funktioniert. Für genau solche Fälle gibt es eine weitere syntaktische Verkürzung.

[»]  Definition

Eine Methodenreferenz ist ein Verweis auf eine Methode, ohne diese jedoch aufzurufen. Syntaktisch trennen zwei Doppelpunkte den Typnamen oder die Referenz auf der linken Seite von dem Methodennamen auf der rechten.

Die Zeile

Arrays.sort( words, (String s1, String s2) -> StringUtils.trimCompare(s1, s2) );

lässt sich mit einer Methodenreferenz abkürzen zu:

Arrays.sort( words, StringUtils::trimCompare );

Im Code steht kein Lambda-Ausdruck mehr, sondern nur noch ein Methodenverweis. Die Sortiermethode erwartet vom Comparator eine Methode, die zwei Strings annimmt und eine Ganzzahl zurückgibt. Der Name der Klasse und der Name der Methode sind unerheblich, weshalb an dieser Stelle eine Methodenreferenz eingesetzt werden kann.

Eine Methodenreferenz ist wie ein Lambda-Ausdruck ein Exemplar einer funktionalen Schnittstelle, jedoch für eine existierende Methode einer bekannten Klasse. Wie üblich bestimmt der Kontext, von welchem Typ genau der Ausdruck ist.

[»]  Hinweis

Gleicher Code für eine Methodenreferenz kann zu komplett unterschiedlichen Typen führen – der Kontext macht den Unterschied:

Comparator<String>                c1 = StringUtils::trimCompare;

BiFunction<String,String,Integer> c2 = StringUtils::trimCompare;
 

Zum Seitenanfang

13.2.3    Varianten von Methodenreferenzen Zur vorigen ÜberschriftZur nächsten Überschrift

Im Beispiel ist die Methode trimCompare(…) statisch, und links vom Doppelpunkt steht der Name eines Typs. Das ist jedoch nicht der einzige Anwendungsfall – insgesamt gibt es drei Varianten für Methodenreferenzen:

Methodenreferenz auf eine …

Syntax für Methodenreferenz

Lambda-Ausdruck

… Klassenmethode

Typ::klassenmethode

() -> Typ.klassenmethode()

(p1) -> Typ.klassenmethode(p1)

(p1[, p2[, …]]) -> Typ.klassenmethode([p1[, p2[, …]])

… Objektmethode

ref::objektmethode

() -> ref.objektmethode()

(p1) -> ref.objektmethode(p1)

([p1[, p2[, …]]) -> ref.objektmethode([p1[, p2[, …]])

… Objektmethode eines Typs

ObjektTyp::objektmethode

(obj) -> obj.objektmethode()

(obj, p1) -> obj.objektmethode(p1)

(obj, p1, p2[, …]]) -> obj.objektmethode([p1[, p2[, …]])

Tabelle 13.6     Unterschiedliche Methodenreferenzen

Tabelle 13.6 macht deutlich, dass mehrere Parameter erlaubt sind. In der Schreibweise für die Methodenreferenz tauchen Parameter nicht auf.

Methodenreferenz auf eine statische Methode

System.currentTimeMillis() liefert ein long mit den Millisekunden seit dem 1.1.1970, 0 Uhr. Das ist auch ein Supplier:

Supplier<Long> time = System::currentTimeMillis;

Math.max(…) ist eine statische Methode, bei der zwei Elemente auf das Maximum reduziert werden. Das ist auch das, was eine BiFunction macht. Daher gilt:

BiFunction<Integer, Integer, Integer> max = Math::max;

Ist eine Hauptmethode in der Klasse JavaApplication mit main(String… args) deklariert, so ist das auch ein Runnable:

Runnable r = JavaApplication::main;

Anders wäre das bei main(String[]): Hier ist ein Parameter zwingend, doch ein Vararg kann auch leer sein.

Methodenreferenz auf eine Objektmethode

System.out ist eine Referenz, und eine Methode wie println(…) kann an einen Consumer gebunden werden. Es ist aber auch ein Runnable, weil es println() auch ohne Parameterliste gibt:

Consumer<String> out = System.out::println;  // s -> System.out.println(s) 

out.accept( "Kates kurze Kleider" );

Runnable out = System.out::println; // () -> System.out.println()

out.run();

Methodenreferenz auf eine Objektmethode eines Typs

Die String-Methode isEmpty() liefert true, wenn der String leer ist, sonst false. Das ist wie ein Predicate. Wir können String::isEmpty statt s -> s.isEmpty() nutzen.

String::length ist ein weiteres Beispiel. Das wäre eine Funktion, die ein String auf ein int abbildet. In Code sieht das so aus: Function<String,Integer> len = String::length.

Um einfach ein Comparator-Objekt aufzubauen, können wir Schlüssel-Extraktoren einsetzen. Diese benötigen eine Funktion (Generics verkürzt):

static <...> Comparator<...> comparing(Function<...> keyExtractor)

Nehmen wir an, wir haben eine Klasse Person und eine Methode getName(). Holen wir die Daten über einen Getter, können wir statt p -> p.getName() die Methodenreferenz Person::getName nutzen.

this und super sind möglich

Anstatt den Namen einer Referenzvariablen zu wählen, kann auch this das Objekt beschreiben, und auch super ist möglich. this ist praktisch, wenn die Implementierung einer funktionalen Schnittstelle auf eine Methode der eigenen Klasse delegieren möchte. Wenn zum Beispiel eine lokale Methode trimCompare(…) in der Klasse existieren würde, in der auch der Lambda-Ausdruck steht, und wenn diese Methode als Comparator in Arrays.sort(…) verwendet werden sollte, könnte es heißen: Arrays.sort(words, this::trimCompare).

Einschränkungen

Es ist nicht möglich, eine spezielle (überladene) Methode über die Methodenreferenz auszuwählen. Eine Angabe wie String::valueOf oder Arrays::sort ist relativ breit – bei Letzterem wählt der Compiler eine der 18 passenden überladenen Methoden aus. Da kann es passieren, dass der Compiler eine falsche Methode auswählt. In dem Fall muss ein expliziter Lambda-Ausdruck eine Mehrdeutigkeit auflösen. Bei generischen Typen kann zum Beispiel List<String>::length oder auch List::length stehen. Auch hier erkennt der Compiler wieder alles selbst.

Was soll das alles?

Einem Einsteiger in die Sprache Java wird dieses Sprachfeature wie der größte Zauber auf Erden vorkommen, und auch Java-Profis bekommen hier zittrige Finger, entweder vor Furcht oder vor Aufregung. In der Vergangenheit musste in Java sehr viel Code explizit geschrieben werden, aber mit diesen neuen Methodenreferenzen erkennt und macht der Compiler vieles von selbst.

Nützlich wird diese Eigenschaft mit den funktionalen Bibliotheken bei der Stream-API, die ein eigenes Kapitel in meinem Buch »Java SE 9 Standard-Bibliothek« einnehmen. Hier nur ein kurzer Vorgeschmack:

Object[] words = { " ", '3', null, "2", 1, "" };

Arrays.stream( words ) // " ", '3', null, "2", 1, ""

.filter( Objects::nonNull ) // " ", '3', "2", 1, ""

.map( Objects::toString ) // " ", "3", "2", "1", ""

.map( String::trim ) // "", "3", "2", "1", ""

.filter( s -> ! s.isEmpty() ) // "3", "2", "1"

.map( Integer::parseInt ) // 3, 2, 1

.sorted() // 1, 2, 3

.forEach( System.out::println ); // 1 2 3

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Spring Boot 3 und Spring Framework 6

Spring Boot 3 und Spring Framework 6




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2024

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern