Java 8 ist nun bald zwei Jahre alt – eine gute Gelegenheit, meine kleine Closures-Reihe weiterzuführen. Nach
Scala,
JavaFX und
JavaScript sind heute also Closures in Java an der Reihe. Das Zähler-Beispiel sieht mit den Lambda-Ausdrücken aus Java 8 so aus:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntSupplier;
public class ClosuresJava8 {
static IntSupplier zaehlerMitStartwert(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
IntSupplier s = () -> {
return n.incrementAndGet();
};
return s;
}
public static void main(String[] args) {
IntSupplier next1 = zaehlerMitStartwert(10);
IntSupplier next2 = zaehlerMitStartwert(20);
System.out.println( next1.getAsInt() ); // 11
System.out.println( next2.getAsInt() ); // 21
System.out.println( next2.getAsInt() ); // 22
System.out.println( next1.getAsInt() ); // 12
}
}
Die Methode
zaehlerMitStartwert()
gibt eine
IntSupplier
-Lambda-Funktion zurück.
IntSupplier ist ein sogenanntes "funktionales Interface", d.h. ein Interface mit genau einer Methode (in diesem Fall
getAsInt()
). Ein "Supplier" bezeichnet dabei eine Methode ohne Parameter, die einen Rückgabewert liefert.
Der
Lambda-Ausdruck ist dabei so etwas wie eine Kurzschreibweise für ein anonymes Objekt des
IntSupplier
-Interfaces, wobei das
new
, der Interface-Name und der Methodenname weggelassen werden. Die Anweisung im Lambda-Ausdruck ("
return n.incrementAndGet();
") wird dabei zum Rumpf der unsichtbaren
getAsInt()
-Methode.
Im Lambda-Ausdruck wird der übergebene Startwert erhöht und zurückgegeben. Allerdings müssen die gebundenen Variablen außerhalb des Lambda-Ausdrucks in Java
final
oder "effectively final" sein (d.h. sie dürfen nicht verändert werden, damit der Compiler
final
annehmen kann). Deshalb lassen wir den Lambda-Ausdruck ein
AtomicInteger-Objekt binden – der Inhalt des
AtomicInteger
-Objektes ist veränderbar, die Referenz darauf ist aber wie gewünscht final.
Im obigen Beispiel wird die Lambda-Funktion nur zum einfacheren Verständnis in der lokalen Variablen
s
zwischengespeichert. Man kann den Lambda-Ausdruck auch direkt als Methodenergebnis zurückgeben:
static IntSupplier zaehlerMitStartwert(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
return () -> {
return n.incrementAndGet();
};
}
Und da unser Lambda-Ausdruck nur aus einer einzigen Anweisung besteht, deren Ergebnis als Funktionsergebnis zurückgegeben wird, kann man das noch weiter kürzen und die geschweiften Klammern und das
return
im Lambda-Rumpf weglassen:
static IntSupplier zaehlerMitStartwert(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
return () -> n.incrementAndGet();
}
Das sieht doch schon fast richtig funktional aus :-)