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

11.6Funktionale Programmierung mit Java Zur vorigen ÜberschriftZur nächsten Überschrift

 

Zum Seitenanfang

11.6.1Programmierparadigmen: imperativ oder deklarativ Zur vorigen ÜberschriftZur nächsten Überschrift

In irgendeiner Weise muss ein Entwickler sein Problem in Programmform beschreiben, damit der Computer es letztendlich ausführen kann. Hier gibt es verschiedene Beschreibungsformen, die wir Programmierparadigmen nennen. Bisher haben wir uns immer mit der imperativen Programmierung beschäftigt, bei der Anweisungen im Mittelpunkt stehen. Wir haben im Deutschen den Imperativ, also die Befehlsform, die sehr gut mit dem Programmierstil vergleichbar ist, denn es handelt sich in beiden Fällen um Anweisungen der Art »tue dies, tue das«. Diese »Befehle« mit Variablen, Fallunterscheidungen, Sprüngen beschreiben das Programm und den Lösungsweg.

Zwar ist imperative Programmierung die technisch älteste, aber nicht die einzige Form, Programme zu beschreiben; es gibt daneben die deklarative Programmierung, die nicht das Wie zur Problemlösung beschreibt, sondern das Was, also was eigentlich gefordert ist, ohne sich in genauen Abläufen zu verstricken. Auf den ersten Blick klingt das abstrakt, aber jeder, der schon einmal

  • eine Selektion wie *.html auf der Kommandozeile/im Explorer-Suchfeld getätigt,

  • eine Datenbankabfrage mit SQL geschrieben,

  • eine XML-Selektion mit XQuery genutzt,

  • ein Build-Skript mit Ant oder make formuliert oder

  • eine XML-Transformation mit XSLT beschrieben hat,

wird das Prinzip kennen.

Bleiben wir kurz bei SQL, um einen Punkt deutlich zu machen. Natürlich führt im Endeffekt die CPU die Abarbeitung der Tabellen und die Auswertungen der Ergebnisse rein imperativ aus, doch es geht um die Programmbeschreibung auf einem höheren Abstraktionsniveau. Deklarative Programme sind üblicherweise wesentlich kürzer, und damit kommen weitere Vorteile wie leichtere Erweiterbarkeit, Verständlichkeit ins Spiel. Da deklarative Programme oftmals einen mathematischen Hintergrund haben, lassen sich die Beschreibungen leichter formal in ihrer Korrektheit beweisen.

Deklarative Programmierung ist ein Programmierstil, und eine deklarative Beschreibung braucht eine Art »Ablaufumgebung«, denn SQL kann zum Beispiel keine CPU direkt ausführen. Aber anstatt nur spezielle Anwendungsfälle wie Datenbank- oder XML-Abfragen zu behandeln, können auch typische Algorithmen deklarativ formuliert werden, und zwar mit funktionaler Programmierung. Damit sind imperative Programme und funktionale Programme gleich mächtig in ihren Möglichkeiten.

 

Zum Seitenanfang

11.6.2Funktionale Programmierung und funktionale Programmiersprachen Zur vorigen ÜberschriftZur nächsten Überschrift

Bei der funktionalen Programmierung stehen Funktionen im Mittelpunkt und ein im Idealfall zustandsloses Verhalten, in dem viel mit Rekursion gearbeitet wird. Ein typisches Beispiel ist die Berechnung der Fakultät. Es ist n! = 1 × 2 × 3 × … × n, und mit Schleifen und Variablen, dem imperativen Weg, sieht sie so aus:

public static int factorial( int n ) {

int result = 1;

for ( int i = 1; i <= n; i++ )

result *= i;

return result;

}

Deutlich sind die vielen Zuweisungen und die Fallunterscheidung durch die Schleife abzulesen, die typischen Indikatoren für imperative Programme. Der Schleifenzähler erhöht sich, damit kommt Zustand in das Programm, denn der aktuelle Index muss ja irgendwo im Speicher gehalten werden. Bei der rekursiven Variante ist das ganz anders, hier gibt es keine Zuweisungen im Programm, und die Schreibweise erinnert an die mathematische Definition:

public static int factorial( int n ) {

return n == 0 ? 1 : n * factorial( n - 1 );

}

Mit der funktionalen Programmierung haben wir eine echte Alternative zur imperativen Programmierung. Die Frage ist nur: Mit welcher Programmiersprache lassen sich funktionale Programme schreiben? Im Grunde mit jeder höheren Programmiersprache! Denn funktional zu programmieren ist ja ein Programmierstil, und Java unterstützt funktionale Programmierung, wie wir am Beispiel mit der Fakultät ablesen können. Da das im Prinzip schon alles ist, stellt sich die Frage, warum funktionale Programmierung einen so schweren Stand hat und bei den Entwicklern gefürchtet ist. Das hat mehrere Gründe:

  • Lesbarkeit: Am Anfang der funktionalen Programmiersprachen steht historisch LISP aus dem Jahr 1958, eine sehr flexible, aber ungewohnt zu lesende Programmiersprache. Unsere Fakultät sieht in LISP so aus:

    (defun factorial (n) (if (= n 1) 1 (* n (factorial (- n 1)))))

    Die ganzen Klammern machen die Programme nicht einfach lesbar, und die Ausdrücke stehen in der Präfix-Notation - n 1 statt der üblichen Infix-Notation n - 1. Bei anderen funktionalen Programmiersprachen ist es anders, dennoch führt das zu einem gewissen Vorurteil, dass alle funktionalen Programmiersprachen schlecht lesbar seien.

  • Performance und Speicherverbrauch: Ohne clevere Optimierungen von Seiten des Compilers und der Laufzeitumgebung führen insbesondere rekursive Aufrufe zu prall gefüllten Stacks und schlechter Laufzeit.

  • Rein funktional: Es gibt funktionale Programmiersprachen, die als »rein« oder »pur« bezeichnet werden und keine Zustandsänderungen erlauben. Die Entwicklung von Ein-/Ausgabeoperationen oder simplen Zufallszahlen ist ein großer Akt, der für normale Entwickler nicht mehr nachvollziehbar ist. Die Konzepte sind kompliziert, doch zum Glück sind die meisten funktionalen Sprachen nicht so rein und erlauben Zustandsänderungen, nur Programmierer versuchen genau diese Zustandsänderungen zu vermeiden, um sich nicht die Nachteile damit einzuhandeln.

  • Funktional mit Java: Wenn es darum geht, nur mit Funktionen zu arbeiten, kommen Entwickler schnell an einen Punkt, an dem Funktionen andere Funktionen als Argumente übergeben oder Funktionen zurückgeben. So etwas lässt sich in Java in der traditionellen Syntax nur sehr umständlich schreiben. Dies führt dazu, dass alles so unlesbar wird, dass der ganze Vorteil der kompakten deklarativen Schreibweise verloren geht.

Aus heutiger Sicht stellt sich eine Kombination aus beiden Konzepten als zukunftsweisend dar. Mit Lambda-Ausdrücken sind funktionale Programme kompakt und relativ gut lesbar, und die JVM hat gute Optimierungsmöglichkeiten. Java ermöglicht beide Programmierparadigmen, und Entwickler können den Weg wählen, der für eine Problemlösung gerade am besten ist. Diese Mehrdeutigkeit schafft natürlich auch Probleme, denn immer wenn es mehrere Lösungswege gibt, entstehen Auseinandersetzungen um die beste der Varianten – und hier kann von Entwickler zu Entwickler eine konträre Meinung herrschen. Funktionale Programmierung hat unbestrittene Vorteile, und das wollen wir uns genau anschauen.

 

Zum Seitenanfang

11.6.3Funktionale Programmierung in Java am Beispiel vom Comparator Zur vorigen ÜberschriftZur nächsten Überschrift

Funktionale Programmierung hat auch daher etwas Akademisches, weil in den Köpfen der Entwickler oftmals dieses Programmierparadigma nur mit mathematischen Funktionen in Verbindung gebracht wird. Und die wenigsten werden tatsächlich Fakultät oder Fibonacci-Zahlen in Programmen benötigen und daher schnell funktionale Programmierung beiseitelegen. Doch diese Vorurteile sind unbegründet, und es ist hilfreich, funktionale Programmierung gedanklich von der Mathematik zu lösen, denn die allermeisten Programme haben nichts mit mathematischen Funktionen im eigentlichen Sinne zu tun, wohl aber viel stärker mit formal beschriebenen Methoden.

Betrachten wir erneut unser Beispiel aus der Einleitung, die Sortierung von Strings, diesmal aus der Sicht eines funktionalen Programmierers. Ein Comparator ist eine einfache »Funktion«, mit zwei Parametern und einer Rückgabe. Diese »Funktion« (realisiert als Methode) wiederum wird an die sort(…)-Methode übergeben. Alles das ist funktionale Programmierung, denn wir programmieren Funktionen und übergeben sie. Drei Beispiele (Generics ausgelassen):

Code

Bedeutung

Comparator c = (c1, c2) ->

Implementiert eine Funktion über einen Lambda-Ausdruck.

Arrays.sort(T[] a, Comparator c)

Nimmt eine Funktion als Argument an.

Collections.reverseOrder(Comparator cmp)

Nimmt eine Funktion an und liefert auch eine zurück.

Tabelle 11.7Beispiele für Funktionen in der Übergabe und als Rückgabe

Funktionen selbst können in Java nicht übergeben werden, also helfen sich Java-Entwickler mit der Möglichkeit, die Funktionalität in eine Methode zu setzen, sodass die Funktion zum Objekt mit einer Methode wird, was die Logik realisiert. Lambda-Ausdrücke bzw. Methoden/Konstruktorreferenzen geben eine kompakte Syntax ohne den Ballast, extra eine Klasse mit einer Methode schreiben zu müssen.

Der Typ Comparator ist eine funktionale Schnittstelle und steht für eine besondere Funktion mit zwei Parametern gleichen Typs und einer Ganzzahl-Rückgabe. Es gibt weitere funktionale Schnittstellen, die etwas flexibler sind als Comparator, in der Weise, dass etwa die Rückgabe statt int auch double oder etwas anderes sein kann.

 

Zum Seitenanfang

11.6.4Lambda-Ausdrücke als Funktionen sehen Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben gesehen, dass sich Lambda-Ausdrücke in einer Syntax formulieren lassen, die folgende allgemeine Form hat:

( LambdaParameter ) -> { Anweisungen }

Der Pfeil macht gut deutlich, dass wir es bei Lambda-Ausdrücken mit Funktionen zu tun haben, die etwas abbilden. Im Fall vom Comparator ist es eine Abbildung von zwei Strings auf eine Ganzzahl – in eine etwas mathematischere Notation gepackt: (String, String) → int.

[zB]Beispiel

Methoden gibt es mit und ohne Rückgabe und mit und ohne Parameter. Genauso ist das mit Lambda-Ausdrücken. Ein paar Beispiele in Java-Code mit ihren Abbildungen:

Lambda-Ausdruck

Abbildung

(int a, int b) -> a + b

(int, int) → int

(int a) -> Math.abs( a )

(int) → int

(String s) -> s.isEmpty()

(String) → boolean

(Collection c) -> c.size()

(Collection) → int

() -> Math.random()

() → double

(String s) -> { System.out.print( s ); }

(String) → void

() -> {}

() → void

Tabelle 11.8Lambda-Ausdrücke und was sie als Funktionen abbilden

Begriff: Funktion versus Methode

Die Java-Sprachdefinition kennt den Begriff »Funktion« nicht, sondern spricht nur von Methoden. Methoden hängen immer an Klassen, und das heißt, dass Methoden immer an einem Kontext hängen. Das ist zentral bei der Objektorientierung, da Methoden auf Attribute lesend und schreibend zugreifen können. Lambda-Ausdrücke wiederum realisieren Funktionen, die erst einmal ihre Arbeitswerte rein aus den Parametern beziehen, sie hängen nicht an Klassen und Objekten. Der Gedanke bei funktionalen Programmiersprachen ist der, ohne Zustände auszukommen, also Funktionen so clever anzuwenden, dass sie ein Ergebnis liefern. Funktionen geben für eine spezifische Parameterkombination immer dasselbe Ergebnis zurück, unabhängig vom Zustand des umgebenden Gesamtprogramms.

 


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