Was sind Lambda Ausdrücke (lambda expressions)?v.8.0
Nur selten oder gar nicht wiederverwendete Klassen werden in Java häufig und gerne als anonyme Klassen geschrieben. Das gängigste Beispiel dürfte die Verwendung eines einfachen ActionListeners sein, der z.B. die Funktionalität eines Buttons steuert:
JButton butt = new JButton("Klick"); butt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Button angeklickt"); } });
Die hier deklarierte anonyme Klasse implementiert das
Interface ActionListener
, das nur eine
einzige Methode besitzt. Seit Java 8.0 werden solche
Interfaces mit genau einer abstrakten Methode als funktionale
Interfaces bezeichnet. Sie müssen zwar
nicht, sollten jedoch mit der Annotation @FunctionalInterface
versehen werden. Um die Methoden solcher Interfaces zu
nutzen, können ab Java 8.0 sog. Lambda
Ausdrücke eingesetzt werden. Das obige Beispiel
kann hiermit auf die folgende Weise umformuliert werden:
butt.addActionListener(e -> System.out.println("Button angeklickt"));
Es fällt auf, dass hier als Argument der Methode addActionListener()
keine vollständige Objektbildung mit new
mehr erfolgt. Stattdessen werden der (beliebige)
Bezeichner des Methodenarguments und die Anweisungen
innerhalb der Parameterklammern von actionPerformed()
getrennt von einem Pfeiloperator ('->') notiert. Es
wird also eine Kurzform der vollständigen
Methodenimplementierung als Parameter der aufgerufenen
Methode actionPerformed()
angegeben.
Dies kann auch mit mehreren Parametern (einer Methode) erfolgen, nicht jedoch bei anonymen Klassen oder Interfaces, die mehrere Methoden enthalten, da die identifizierende Methodensignatur ja nicht notiert wird. Besitzt eine Methode keinen Parameter, so werden lediglich die leeren runden Klammern notiert:
public class LambdaHello { @FunctionalInterface interface Printer { void print(); } public static void main(String[] args) { Printer p = () -> System.out.println("Hallo Welt!"); p.print(); // "Hallo Welt!" } }
Man erkennt hier, dass Objektbildung und Speicherung in
einer Variablen, sowie Methodenimplementierung im
einfachsten Fall gerade einmal eine Zeile
benötigen.
Das folgende Beispiel zeigt, wie dies
mit der Definition eines eigenen Interfaces, zwei
Methodenparametern und zwei Anweisungen formuliert wird:
public class LambdaPrintTest { @FunctionalInterface interface Printer { public void print(String s, String t); } public static void main(String[] args) { Printer p = (s, t) -> { t = t.toUpperCase(); System.out.println(s + " " + t); }; p.print("Hallo", "Welt"); // Hallo WELT } }
Sollen mehrere Anweisungen in der Methode
ausgeführt werden, so werden sie in geschweifte
Klammern eingeschlossen und jeweils durch Semikolon
abgeschlossen. Bei nur einer Anweisung kann der
Einschluss in Klammern entfallen.
Besitzt die Methode
einen Rückgabewert, so wird dieser als letztes
notiert, ebenfalls durch Semikolon abgeschlossen:
public class LambdaCalculateTest { @FunctionalInterface interface Rectangle { public double getArea(double width, double height); } public static void main(String[] args) { Rectangle rect = (w, h) -> { w*=2; h*=3; return w * h; }; System.out.println("Flaeche: " + rect.getArea(4, 3)); } }
Neben der vereinfachten Schreibweise einer anonymen
Klasse erlauben Lambda-Ausdrücke auch andere
Zugriffsberechtigungen auf Variablen. So kann innerhalb
eines Lambda-Ausdrucks direkt auf in der gleichen
Codeebene deklarierte Variablen zugegriffen werden,
sofern diese final
deklariert oder
zumindest effektiv final
sind. Im folgenden
Fall kann deshalb auf die explizite final
-Deklaration
von multi
verzichtet werden.
public class LambdaCalculateTest { @FunctionalInterface interface Rectangle { public double getArea(double width, double height); } public static void main(String[] args) { double multi = 3; Rectangle rect = (w, h) -> { w*=multi; h*=multi; return w * h; }; System.out.println("Flaeche:" + rect.getArea(4, 3)); } }
Allerdings sollte in solchen Fällen vorsichtshalber
nicht auf eine final
-Deklaration verzichtet
werden, denn ein Code wie der folgende ist nicht
möglich:
//... double multi = 3; multi = 8; Rectangle rect = (w, h) -> { w*=multi; h*=multi; return w * h; }; // Fehler //...
Vertiefung
Java ist eine objektorientierte Programmiersprache. Der Zustand von Objekten bestimmt den Status eines Programms, wobei Funktionen die Eigenschaften der Objekte modifizieren und den Programmablauf steuern.
Lambda-Ausdrücke führen, zusammen mit
funktionalen Interfaces, das Paradigma der funktionalen
Programmierung in Java ein. Hierbei dienen Funktionen
nicht mehr dazu, den Status von vorhandenen Objekten zu
modifizieren, vielmehr wird der Programmablauf
weitgehend von Objekten getrennt.
Greifen wir auf
obiges Beispiel zurück:
@FunctionalInterface interface Rectangle { public double getArea(double width, double height); }
Da Rectangle
ein Interface ist, kann kein
Objekt dieses Typs mit new
erzeugt werden.
Die Verwendung der Methode getArea()
ist in
der OOP jedoch nur möglich, nachdem eine Klasse,
die Rectangle
implementiert, erzeugt und
innerhalb dieser die Methode konkretisiert (mit einem
Methodenkörper versehen) wird. Nach
anschließendem Erzeugen des Objektes muss auf
diesem die Methode aufgerufen werden. Sie liefert dann
die Fläche als Eigenschaft des Objektes.
Bei der
Verwendung eines Lambda-Ausdurcks wird dieses Prinzip
verlassen.
Rectangle rect = (w, h) -> { w*=2; h*=3; return w * h; };
Anstatt eines Konstruktoraufrufs befindet sich hier auf
der rechten Seite vom Zuweisungsoperator eine Methode,
die unabhängig von einem implementierenden Objekt
verwendet wird. Sie liefert ein eigenständiges Rectangle
-Objekt,
das zum Zeitpunkt seiner Verwendung gebildet und
anschließend wieder verworfen werden kann.
Quellen
http://www.tutego.de/blog/javainsel/2014/03/java-8-umgebung-lambda-ausdruecke-variablenzugriffe/
https://stackabuse.com/guide-to-functional-interfaces-and-lambda-expressions-in-java/
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.