Was sind Lambda Ausdrücke (lambda expressions)?v.8.0

Lambda Ausdrücke helfen, die oft schlecht lesbaren, langen Deklarationen innerer, anonymer Klassen zu vermeiden.

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 Methode als funktionale Interfaces bezeichnet. Um deren Methoden 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. Statt dessen 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 {
    
    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 funktioniert:

public class LambdaPrintTest {
    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 auch entfallen.
Besitzt die Methode einen Rückgabewert, so wird dieser als letztes notiert, ebenfalls durch Semikolon abgeschlossen:

public class LambdaCalculateTest {
    interface Rectangle {
        public double getArea(double length, double width);
    }

    public static void main(String[] args) {
        Rectangle rect = (l, w) -> { l*=2; w*=3; return l * w; };
        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 sind.

public class LambdaPrintTest {
    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
    }
}