Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 
Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Komponenten, JavaBeans und Module
14 Die Klassenbibliothek
15 Einführung in die nebenläufige Programmierung
16 Einführung in Datenstrukturen und Algorithmen
17 Einführung in grafische Oberflächen
18 Einführung in Dateien und Datenströme
19 Einführung ins Datenbankmanagement mit JDBC
20 Einführung in <XML>
21 Testen mit JUnit
22 Bits und Bytes und Mathematisches
23 Die Werkzeuge des JDK
A Java SE-Paketübersicht
Stichwortverzeichnis


Download:

- Beispielprogramme, ca. 35,4 MB


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 11 Lambda-Ausdrücke und funktionale Programmierung
Pfeil 11.1 Code = Daten
Pfeil 11.2 Funktionale Schnittstellen und Lambda-Ausdrücke im Detail
Pfeil 11.2.1 Funktionale Schnittstellen
Pfeil 11.2.2 Typ eines Lambda-Ausdrucks ergibt sich durch Zieltyp
Pfeil 11.2.3 Annotation @FunctionalInterface
Pfeil 11.2.4 Syntax für Lambda-Ausdrücke
Pfeil 11.2.5 Die Umgebung der Lambda-Ausdrücke und Variablenzugriffe
Pfeil 11.2.6 Ausnahmen in Lambda-Ausdrücken
Pfeil 11.2.7 Klassen mit einer abstrakten Methode als funktionale Schnittstelle? *
Pfeil 11.3 Methodenreferenz
Pfeil 11.3.1 Varianten von Methodenreferenzen
Pfeil 11.4 Konstruktorreferenz
Pfeil 11.4.1 Standard- und parametrisierte Konstruktoren
Pfeil 11.4.2 Nützliche vordefinierte Schnittstellen für Konstruktorreferenzen
Pfeil 11.5 Implementierung von Lambda-Ausdrücken *
Pfeil 11.6 Funktionale Programmierung mit Java
Pfeil 11.6.1 Programmierparadigmen: imperativ oder deklarativ
Pfeil 11.6.2 Funktionale Programmierung und funktionale Programmiersprachen
Pfeil 11.6.3 Funktionale Programmierung in Java am Beispiel vom Comparator
Pfeil 11.6.4 Lambda-Ausdrücke als Funktionen sehen
Pfeil 11.7 Funktionale Schnittstelle aus dem java.util.function-Paket
Pfeil 11.7.1 Blöcke mit Code und die funktionale Schnittstelle java.util.function.Consumer
Pfeil 11.7.2 Supplier
Pfeil 11.7.3 Prädikate und java.util.function.Predicate
Pfeil 11.7.4 Funktionen und die allgemeine funktionale Schnittstelle java.util.function.Function
Pfeil 11.7.5 Ein bisschen Bi …
Pfeil 11.7.6 Funktionale Schnittstellen mit Primitiven
Pfeil 11.8 Optional ist keine Nullnummer
Pfeil 11.8.1 Optional-Typ
Pfeil 11.8.2 Primitive optionale Typen
Pfeil 11.8.3 Erstmal funktional mit Optional
Pfeil 11.9 Was ist jetzt so funktional?
Pfeil 11.10 Zum Weiterlesen
 

Zum Seitenanfang

11Lambda-Ausdrücke und funktionale Programmierung Zur vorigen ÜberschriftZur nächsten Überschrift

»EDV-Systeme verarbeiten, womit sie gefüttert werden.

Kommt Mist rein, kommt Mist raus.«

– André Kostolany (1906–1999)

Bei der Entwicklung von Maschinensprache (bzw. Assembler) hin zur Hochsprache ist eine interessante Geschichte der Parametrisierung abzulesen. Schon die ersten Hochsprachen erlaubten eine Parametrisierung von Funktionen mit unterschiedlichen Argumenten. Die Programmiersprache Java, die im Jahr 1996 geboren wurde, bot das von Anfang an, da sie erst mehrere Jahrzehnte nach den ersten Hochsprachen entstand. Relativ spät folgten dann die Generics. Die Parametrisierung des Typs wurde erst 2004 mit der Version 5 realisiert. Bis dahin konnte eine Liste zum Beispiel Zeichenketten ebenso enthalten wie Zwerge (als Java-Objekte). Funktionale Programmierung ermöglichte nun eine Parametrisierung des Verhaltens; eine Sortiermethode arbeitet immer gleich, aber ihr Verhalten bei den Vergleichen wird angepasst. Das ist eine ganz andere Qualität, als unterschiedliche Werte zu übergeben. Das bietet nun seit 2014 die Version Java 8 elegant und einfach mit den so genannten Lambda-Ausdrücken.

 

Zum Seitenanfang

11.1Code = Daten Zur vorigen ÜberschriftZur nächsten Überschrift

Wer den Begriff »Daten« hört, denkt zunächst einmal an Zahlen, Bytes, Zeichenketten oder auch komplexe Objekte mit ihrem Zustand. Wir wollen in diesem Kapitel diese Sicht ein wenig erweitern und auf Programmcode lenken. Java-Code, versinnbildlicht als Serie von Bytecodes, besteht auch aus Daten. Und wenn wir uns einmal auf diese Sichtweise einlassen, dass Code gleich Daten ist, dann lässt sich Code auch wie Daten übergeben und so von einem Punkt zum anderen übertragen, speichern und später referenzieren. Mit dieser Möglichkeit, Code zu übertragen, lässt sich das Verhalten von Algorithmen leicht anpassen. Beginnen wir mit ein paar Beispielen, bei denen Programmcode übergeben wird, auf den dann später zugegriffen wird:

  • Ein Thread führt Programmcode im Hintergrund aus. Der Programmcode, den der Java-Thread ausführen soll, wird in ein Objekt vom Typ Runnable verpackt, genau genommen in eine run()-Methode gesetzt. Kommt der Thread zum Zuge, ruft er die run()-Methode auf.

  • Ein Timer ist eine java.util-Klasse, die zu bestimmen Zeitpunkten Programmcode ausführen kann. Der Objektmethode scheduleAtFixedRate(…) wird dabei ein Objekt vom Typ TimerTask übergeben, das den Programmcode enthält.

  • Zum Sortieren von Daten kann eine eigene Ordnung definiert werden, die dem Sortierer als Comparator übergeben werden kann. Der Comparator deklariert eine Vergleichsmethode, an die sich der Sortierer wendet, um zwei Objekte in die gewünschte Reihenfolge zu bringen.

  • Aktiviert der Benutzer auf der Oberfläche eine Schaltfläche, so führt das zu einer Aktion. Der Programmcode steckt – beim UI-Framework Swing – in einem Objekt vom Typ ActionListener und wird an der Schaltfläche JButton mit addActionListener(…) fest gemacht. Kommt es zu einer Schaltflächenaktivierung, arbeitet das UI-System den Programmcode in der Methode actionPerformed(…) des gespeicherten ActionListener ab.

Um Programmcode von einer Stelle zur anderen zu bringen, wird in Java immer der gleiche Mechanismus eingesetzt: Eine Klasse implementiert eine (in der Regel nichtstatische) Methode, in der der auszuführende Programmcode steht. Ein Objekt dieser Klasse wird an eine andere Stelle übergeben, und der Interessent greift dann über die Methode auf den Programmcode zu. Dass ein Objekt noch mehr als diese eine Implementierung enthalten kann, etwa Variablen, Konstanten, Konstruktoren, ist dafür nicht relevant. Diesen Mechanismus schauen wir uns jetzt in verschiedenen Varianten genauer an.

Innere Klassen als Code-Transporter

Bleiben wir bei dem Beispiel mit den Vergleichen. Angenommen, wir sollen Strings so sortieren, dass Leerraum vorne und hinten bei den Vergleichen ignoriert wird, also " Newton " gleich "Newton" ist. Bei Vorgaben dieser Art muss einem Sortieralgorithmus ein Stückchen Code übergeben werden, damit er die korrekte Reihenfolge herstellen kann. Praktisch sieht das so aus:

Listing 11.1CompareTrimmedStrings.java

import java.util.*;

public class CompareTrimmedStrings {

public static void main( String[] args ) {

class TrimmingComparator implements Comparator<String> {

@Override public int compare( String s1, String s2 ) {

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

}

}

String[] words = { "M", "\nSkyfall", " Q", "\t\tAdele\t" };

Arrays.sort( words, new TrimmingComparator() );

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

}

}

Die Ausgabe ist:

[ Adele , M, Q,

Skyfall]

Der TrimmingComparator enthält in der compare(…)-Methode den Programmcode für die Vergleichslogik. Ein Exemplar vom TrimmingComparator wird aufgebaut und Arrays.sort(…) übergeben. Das geht mit weniger Code!

Innere anonyme Klassen als Code-Transporter

Klassen enthalten Programmcode, und Exemplare der Klassen werden an Methoden wie sort(…) übergeben, damit der Programmcode dort hinkommt, wo er gebraucht wird. Doch elegant ist das nicht. Für die Beschreibung des Programmcodes ist extra eine eigene Klasse erforderlich. Das ist viel Schreibarbeit, und über eine innere anonyme Klasse lässt sich der Programmcode schon ein wenig verkürzen:

String[] words = { "M", "\nSkyfall", " Q", "\t\tAdele\t" };

Arrays.sort( words, new Comparator<String>() {

@Override public int compare( String s1, String s2 ) {

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

} } );

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

Allerdings ist das immer noch aufwändig: Wir müssen eine Methode überschreiben und dann ein Objekt aufbauen. Für Programmautoren ist das lästig, und die JVM hat es mit vielen überflüssigen Klassendeklarationen zu tun. Die Frage ist: Wenn der Compiler weiß, dass bei sort(…) ein Comparator nötig ist, und wenn ein Comparator sowieso nur eine Methode hat, muss dann Comparator und compare(…) überhaupt genannt werden?

Abkürzende Schreibweise durch Lambda-Ausdrücke

Ab Java 8 lässt sich Programmcode leichter an eine Methode übergeben, denn es gibt eine kompakte Syntax für die Implementierung von Schnittstellen mit einer Operation. Für unser Beispiel sieht das so aus:

String[] words = { "M", "\nSkyfall", " Q", "\t\tAdele\t" };

Arrays.sort( words,

(String s1, String s2) -> { return s1.trim().compareTo(s2.trim()); } );

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

Der in fett gesetzte Ausdruck nennt sich Lambda-Ausdruck. Er ist eine kompakte Art und Weise, Schnittstellen mit genau einer Methode zu implementieren; die Schnittstelle Comparator hat genau eine Operation compare(…).

Optisch sind sich ein Lambda-Ausdruck und eine Methodendeklaration ähnlich; was wegfällt sind Modifizierer, der Rückgabetyp, der Methodenname und (mögliche) throws-Klauseln.

Methodendeklaration

Lambda-Ausdruck

public int compare

( String s1, String s2 )



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


( String s1, String s2 )

->

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

Tabelle 11.1Vergleich der Methodendeklaration einer Schnittstelle mit dem Lambda-Ausdruck

Wenn wir uns den Lambda-Ausdruck als Implementierung dieser Schnittstelle anschauen, dann lässt sich dort nichts von Comparator oder compare(…) ablesen – ein Lambda-Ausdruck repräsentiert mehr oder weniger nur den Java-Code und lässt das, was der Compiler aus dem Kontext herleiten kann, weg.

Alle Lambda-Ausdrücke lassen sich in einer Syntax formulieren, die die folgende allgemeine Form hat:

( LambdaParameter ) -> { Anweisungen }

Lambda-Parameter sind sozusagen die Eingabewerte für die Anweisungen. Die Parameterliste wird so deklariert, wie von Methoden oder Konstruktoren bekannt, allerdings gibt es keine Varargs. Es gibt syntaktische Abkürzungen, wie wir später sehen werden, doch vorerst bleiben wir bei dieser Schreibweise.

Geschichte

Der Java-Begriff »Lambda-Ausdruck« geht auf das Lambda-Kalkül (in der englischen Literatur Lambda calculus genannt, auch geschrieben als λ-calculus) aus den 1930er Jahren zurück und ist eine formale Sprache zur Untersuchung von Funktionen.

 


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
 Zum Katalog
Zum Katalog: Java ist auch eine Insel Java ist auch eine Insel

Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?

Ihre Meinung



 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel

Java ist auch eine Insel




Zum Katalog: Java SE 9-Standard-Bibliothek

Java SE 9-Standard-Bibliothek




Zum Katalog: Professionell entwickeln mit Java EE 8

Professionell entwickeln mit Java EE 8




Zum Katalog: Entwurfsmuster

Entwurfsmuster




Zum Katalog: IT-Projektmanagement

IT-Projektmanagement




 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2017

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