JAVA-C1
Funktionale Programmierung
@! Spungmarken in allen Pages testen!
Funktionale Programmierung
Function as Value
Pure Function
Higher-Order Functions
@FunctionalInterface Annotation
Methoden-Referenzen
Bedeutung von Stream API
Optional als neuer Typ
Zusammengefasst
Funktionales Interface
@FunctionalInterface
Lambda-Expressions
Simple / Komplex
Scope
Custom-Functional-Interfaces
Custom-Functional-Interfaces
Custom-Functional-Interfaces
Herkömmliches Interface
Funktionales Interface IRechnerDouble definieren und verwenden.
Generische funktionale Interfaces
@FunctionalInterface
Beispiele mit IRechnerGenericFlexible<Integer> r = ..
Beispiel: Lambda-Rechenoperationen mit Objekten
Beispiel: IRead T, IUpdate T, I Read T, IPrint T
Predefined Functional Interfaces
Overview
Function
BiFunction
Predicate
Supplier
Consumer
Diagramm aller Predefined Functional Interfaces
Diagramm Converter Interfaces
ToIntFunction/ToIntBiFunction
DoubleBinaryOperator (EXAMPLE)
@! Sprunmarken ergänzen/testen!
plugin-and-command-pattern-architecture
Beispiel/Collection
Überblick
Basics | Overview
Mit diesem Themenblock beginnen wir eine Einführung in die sogenannte Funktionale Programmierung oder in (engl.) Functional Programming mit Java, Pure Functions, Immutability, Higher-Order-Functions und den Ausblick auf die Bedeutung für Methoden-Referenzen, Stream API, Optionals und mehr.
Die Bedeutung der Function in objektorientierten
Programmiersprachen wie Java oder C# .NET wurde lange Zeit auf Methoden
unter dem Aspekt der Objektorientierung beschränkt. Sequenzen von Programmschritten
selbst gilt es aber davon unabhängig als Wert zu verstehen, um diese Funktionen
auch als Werte übergeben bzw. referenzieren zu können. Wir geben hier den
Überblick was man zu Funktionalen Interfaces, Lambdas, Methoden-Referenzen,
Optionals im Mindestumfang wissen sollte.
Basics | Was ist funktionale Programmierung?
Funktionale Programmierung (Functional Programming) ist ein Programmierparadigma, das den Schwerpunkt auf die Verwendung von Funktionen als die zentralen Bausteine einer Software legt. Der Ansatz bricht in vielen Bereichen mit der mit der bislang üblichen imperativen Programmierung und führt damit zu einem Umdenken in allen Programmiersprachen, nicht nur Java.
Anstatt in erster Linie Zustandsänderungen und sequentielle Schritt-für-Schritt-Anweisungen (imperative Programmierung) zu verwenden, konzentriert man sich hier auf das Beschreiben von Berechnungen in Form von Funktionen, die als "erste Klasse" (engl. First Class) behandelt werden. Das bedeutet, dass Funktionen wie Werte behandelt, als Parameter übergeben, als Rückgabewert verwendet und in Datenstrukturen gespeichert werden können.
Basics | Konzepte und Techniken
Pure Functions
«Pure Functions» sind «pure Funktionen» oder «reine Funktionen» dahingehend, dass diese keine sogenannten «Site-Effects» haben. Führt man die Funktion mit einem bisher schon einmal übergebenen Input aus so kommt auch immer der selbe Output heraus.
Ein Site-Effect bestände als Nebenwirkung bei einer Ausführung der Funktion darin, dass die Nutzung der Funktion irgendetwas im System verändert. Eine (engl.) «Pure Function» vermeidet also ds Ändern von Zuständen und globalen Variablen im System.
Immutability (Unveränderlichkeit)
Daten werden nicht verändert. Wird eine Veränderung von Daten erforderlich, wird stattdessen ein neuer Zustand erzeugt. In Java kann dies unter anderem durch den Einsatz von final-Variablen und unveränderlichen Objekten (wie beispielsweise Collections aus der Java-Collections-API) erreicht werden.
Die Java Syntax unterstützt dieses Prinzip dahingehend, dass man
Variablen final deklariert und
auch bei Collections aus der Java-Collection-API dann Collections
nutzt, welche unveränderliche Objekte bleiben, so dass jeder
Versuch, diese Daten zu verändern, grundsätzlich scheitert.
Higher-Order Functions
In funktionalen Sprachen können Funktionen als Parameter übergeben oder als Rückgabewert von anderen Funktionen geliefert werden. Man spricht von Higher-Order-Functions, dt. «Funktionen einer höhere Ordnung».
In Java ist dieses möglich mit Hilfe von «Lambda-Ausdrücken» sowie sogenannter «Funktionalen Interfaces».
Funktionale Interfaces lassen sich aus sogenannte Custom Function Interfaces wie auch Interfaces im Kontext normaler Klassen individuell programmieren und sind mitunter an der «@FunctionalInterface» Annotation zu erkennen.
Das Java Package java.util.function bietet allerdings
schon eine Reihe funktionaler Interfaces für oft benötigte Zielsetzungen wie mitunter
Predicate<T>,
Function<T,R>,
BiFunction<T,U,R>,
Consumer<T>,
Supplier<T,R>
und einige weitere an. Die Nutzung dieser Interfaces besticht
dadurch, dass andere Entwickler anhand dieser Typen deren
Funktionsweise bereits kennen.
Lambda Expressions
Seit Java 8 ermöglichen Lambda-Ausdrücke wie beispielsweise der Ausdruck
addOne=x->x+1
eine komprimierte Syntax zur Darstellung von Funktionalität.
Anstatt anonyme Klassen zu schreiben, verwendet man Lambda-Ausdrücke, die es erleichtern,
Code im funktionalen Stil zu formulieren.
Um diese Ausdrücke aber dann auch in Variablen speichern
oder über Parameter und Return-Werte übergeben zu können,
sind die vorgenannten Funktionalen Interfaces wie mitunter
hier der Typ Function<Integer, Integer>
erforderlich.
Method References
Java ermöglicht darüber hinaus im Zusammenhang mit der funktionalen Programmierung sogenannte Methoden-Referenzen, engl. «Method Reference».
Mit f = System.out::println> kann man beispielsweise
die Referenz auf die Print-Line-Funktion in einer Variablen f
speichern. Die Voraussetzung hierfür ist allerdings, dass die Variable f
dann auch einen entsprechenden Typ eines funktionalen Interfaces wie z. B.
Consumer<String> f;> in der Deklaration
der Variablen bekommen hat.
Eine Consumer-artige Funktion verarbeitet einen Eingabewert,
liefert aber keinen Rückgabewert, dh. der Rückgabewert ist quasi void.
Stream API
Die Java Streams API (eingeführt in Java 8) ist ein bedeutendes Werkzeug für die funktionale Verarbeitung von Collections und anderen Datenströmen.
Mithilfe der mit JAVA 8 eingeführten Streams lassen sich Operationen wie
Filter filter(..),
Mapping map(..),
Reduktion reduce(..) in der Verarbeitung von Daten deklarativ formulieren.
Eine Besonderheit in diesem Kontext ist insbesondere die
parallelStream(..) Methode
mit deren Hilfe sich die Daten nicht nur sequentiell sondern auch
parallel verarbeiten lassen, ohne dass man die zugehörigen Threads
alle selbst anlegen muss.
Diese sehr leicht und einfach wirkende Verwendung mehrerer Prozessoren in der parallelen Verarbeitung von Daten können allerdings bei Funktionen, welche keine puren Funktionen sondern Funktionen mit Site-Effects sind, zu Nebenwirkungen führen, weil die Reihenfolge der parallen Verarbeitung von Daten in diesem Falle dann zu verschiedenen Ergebnissen führen könnte. Diesen Fall sollte man um jeden Preis tunlichst vermeiden, damit für die Automatisierung von Tests sichergestellt ist, dass die selben Daten in der Verarbeitung auch stets zum selben Ergebnis führen.
Optionals
Ein Optional ist ein Container-Objekt
welches entweder einen Wert enthält oder aber leer ist.
Diese Optionals erleichtern den Umfang mit null-Werten und dienen dazu, Site-Effects zu minimieren.
Zusammenfassend lässt sich sagen, dass die «Funktionale Programmierung» dank der mit Java 8 eingeführten Features zu einem festen Bestandteil der modernen Programmierung von Anwendungen geworden ist und dahingehend nicht nur in der Programmiersprache Java sondern auch in anderen Programmiersprachen wie C# und JavaScript zu finden sind.
Funktionale Programmierung in Java bedeutet, den Code deklarativ zu gestalten, indem man sich auf reine Funktionen, Unveränderlichkeit und die Verarbeitung von Datenströmen mittels Lambda-Ausdrücken und der Streams API fokussiert. Diese Techniken helfen, Nebenwirkungen zu reduzieren, Modelle klarer zu strukturieren und Code leichter testbar und wartbar zu machen.
Basics | Links
FootNotes
Functional Programming, Funktionale Programmierung
UIO3 Es ist einfacher als Du denkst.
Stelle noch heute Deine Anfrage.
