Java 8 – Lambda Expressions

Mit Java 8 halten Lambda Expressions Einzug in die Java Welt. Ein Lambda Ausdruck umfasst dabei Funktionalität, die nicht benannt (eine eigene Methode) oder einer Klasse zugeordnet ist, sondern Code der gespeichert und ausgeführt werden kann. Dabei werden Lambdas wie Argumente an Operationen übergeben, die die Funktionalität später ausführen. Ein viel zitiertes Beispiel aus der Stream API (auch eine Neuerung in Java 8) ist:

Stream.of(1,2,3,4,5).forEach({x –> System.out.print(x)})

Dabei wendet die forEach Methode die Lambda Expression an jedem Element der Sequenz an.

x –> System.out.print(x) ist der Lambda Ausdruck. Erst hat er wie eine Methode eine Parameterliste (in diesem Falle nur x). Diese wird mit einem Pfeilsymbol –> an einen geklammerten Lambda-Body übergeben. Wie bei einer anonymen Klasse fehlt der Methodenname. Returntype und eventuelle throws Klausel deduziert der Compiler automatisch aus dem Lambda-Body.

Warum?

Das mag am Anfang etwas befremdlich aussehen. Der Vorteil erschließt sich einem jedoch, wenn man die weiteren neuen Sprachkonstrukte wie

  • default Methoden in Interfaces,
  • @FunctionalInterface
  • oder aber die neue Stream API
  • Methodenreferenzen

hinzuzieht.

Ich möchte das Anhand eines Beispiels illustrieren. Seit Java 1.5 möchte man den extended for loop nicht mehr missen um über die Elemente einer Collection zu iterieren

List<String> foobarList = Arrays.asList("foo","bar","foobar");
for (String foo : myList) {
  System.out.println(foo);
}

Eigentlich schön kurz und übersichtlich, jedoch ist die Umsetzung der Iteration an den Ort des Aufrufes gebunden. Auch kann der extended for loop nicht für eine Paralellisierung herangezogen werden. Dazu müsste man die Liste vorerst in kleinere Teillisten zerlegen und diese dann in unterschiedlichen Threads abarbeiten. Natürlich könnte man sich dafür wieder eine Util Klasse schreiben oder sich eine bereits vorhandene Lösung suchen. Aber es wäre schon schön wenn man das einfach der List überalssen könnte. Zu diesem Zweck wurden default Methoden in Interfaces eingeführt und das Iterable Interface mit einer forEach(Consumer<? superT> action) Methode erweitert.

foobarList.forEach(new Consumer<String>() {
   public void accept(String foo) {
      System.out.println(foo);
   }
});

Der Code hat dabei zwar an Länge zugelegt, aber es hat auch noch die Übersichtlichkeit darunter gelitten. Großes Plus dabei ist natürlich die saubere Trennung von Logik für die Iteration von der Logik für die Verarbeitung des einzelnen Elements. Parallelisierung wir kommen!

Aber es steckt schon eine Menge unübersichtlicher Boilerplate-Code drum herum. Verpacken wir das nun jedoch in die Lambda-Notation habe wir ein übersichtliches Stück Code:

foobarList.forEach((String foo) -> System.out.println(foo));

In diesem speziellen Fall lässt sich der Code noch auf ein Minimum reduzieren, da er nur einen einzelnen Parameter hat und wir sogenannte Methodenreferenzen nutzen können. Diese sind ebenfalls mit Java 8 und den Lambda Ausdrücken im Sprachraum Java eingeführt worden.

myList.forEach(System.out::print);

Nice!

Weiterführende Literatur

Die von Oracle angedachten Anwendungsbereiche sowie eine ausführliche Einführung findet man unter http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Wer sich lieber berieseln lassen mag, dem seien die Videos der Oracle Learning Library empfohlen. Hier das kurze Teaser Video dazu:

Ähnliche Artikel: