Skip to main content

Lambda-Ausdrücke und Methodenreferenzen

Wir haben bereits gesehen, dass wir Funktionen mithilfe von Funktionsobjekten in einer objektorientierten Programmiersprache wie Java simulieren können. Die Grundidee dahinter ist ein Interface Function<T, R> zu definieren, welches Funktionen modelliert. In diesem ist eine Methode definiert, die Werte vom Typ T zu Werten vom Typ R abbildet:

interface Function<T, R> {
R apply(T x);
}

Dieses Interface ist bereits im Package java.util.function der Java Standardbibliothek definiert. Wir können dies also für unsere Methodensignaturen verwenden, ohne dies selber schreiben zu müssen. Eine viel grössere Erleichterung für die funktionale Programmierung, ermöglicht Java aber durch Lambda-Ausdrücken.

Lambda-Ausdrücke

Bisher mussten wir für Funktion jeweils eine Klasse schreiben, die das Interface Function implementiert. Dies ist sowohl mühsam als auch unflexibel. Lambda Ausdrücke erlauben uns hingegen, so eine Funktionsdefinition sehr kompakt zu schreiben. Betrachten wir nochmals das Beispiel der Funktion square. Wir könnten diese wie folgt definieren:

class SquareFunction implements Function<Integer, Integer> {
public Integer apply(Integer value) {
return x * x;
}
}

und danach eine konkrete Instanz erstellen:

Function<Integer, Integer> squareFun = new SquareFunction();

Mittels Lambda Ausdrücken reduziert sich dieser ganze Code jedoch zu dem folgenden Einzeiler:

Function<Integer, Integer> squareFun = (Integer x) -> { return x * x };

Allgemeiner formuliert entspricht der lambda-Ausdruck (T value) -> { AUSDRUCK } also dem folgenden Code:

class LambdaExpression implements Function<Integer, Integer> {
public R apply(T value) {
AUSDRUCK;
}
}

Vereinfachungen und Funktionen mehrerer Argumente

Die obige Schreibweise ist schon sehr kurz. In vielen Fällen kann man den Ausdruck aber noch stark verkürzen. Einerseits dürfen wir den Datentyp des Arguments weglassen, wenn dies aus dem Kontext klar wird. Andererseits können wir auch das Schlüsselwort return weglassen. Auch Klammern dürfen wir in vielen Fällen weglassen. Folgendes wäre also gültig:

Function<Integer, Integer> squareFun = x -> x * x;

Es ist auch möglich, Lambda-Ausdrücke mit mehreren Argumenten zu schreiben. Das entsprechende Interface in Java heisst BiFunction und ist wie folgt definiert:

interface BiFunction<T, U, R> {
R apply(T x, U y);
}

Ein entsprechender Lambda-Ausdruck wäre zum Beispiel:

BiFunction<Integer, Integer, Integer> plusFun = (Integer a, Integer b) -> a + b;
Experimente
  • Erzeugen Sie Lambda-Ausdrücke für Ihre eigenen Berechnungen
  • Experimentieren Sie mit Lambda-Ausdrücken mit unterschiedlicher Anzahl von Parametern
  • Können Sie eine Methode compose schreiben, die zwei Funktionen entgegennimmt und eine neue Funktion zurückgibt, welche diese zwei Funktionen hintereinander ausführt (also aus f:IRf: I \to R und g:ROg : R \to O eine Funktion h:IOh : I \to O mit h(x)=g(f(x))h(x) = g(f(x)) definiert.

Methodenreferenzen

Neben Lambda-Ausdrücken können auch Methoden mit der richtigen Signatur als Wert behandelt werden. Eine Methode mit nur einem Argument könnte also einer Variable mit Typ Function<T, R> zugewiesen werden. Analog dazu können wir eine Methode mit zwei Argumenten einer Variable mit Typ BiFunction<T, U, R> zuweisen. Um die Methode als Wert zu behandeln, schreiben wir den Objektnamen, gefolgt von :: und dem Methodennamen. Dies ist im Folgenden illustriert:

class MethodExample {
Integer square(Integer x) {
return x * x;
}

public static void main(String[] args) {
MethodeExample instance = new MethodeExample();

Function<Integer, Integer> squareFun = instance::square;
}
}

Bei Klassenmethoden würden wir entsprechend einfach den Klassennamen scheiben:

class MethodExample {
static Integer square(Integer x) {
return x * x;
}

public static void main(String[] args) {

Function<Integer, Integer> squareFun = MethodeExample::square;
}
}

Experimente

  • Experimentieren Sie mit Methoden mit unterschiedlichen Typen von Argumenten und Rückgabewert. Wie ist es mit der Typsicherheit? Dürfen Sie Methoden, die nicht den richtigen Typ haben an eine Methode als Argument übergeben?
  • Definieren Sie ihre eigenen, komplexeren Methoden mit mehreren (verschiedenen) Argumenten. Versuchen Sie diese als Methodenreferenz einer anderen Methode zu übergeben. (Hinweis, eventuell müssen Sie ihr eigenes Interface schreiben, welche eine Funktion mit der gewünschten Anzahl Argumente repräsentiert.)

Haben Sie Fragen oder Bemerkungen? Schreiben Sie diese doch ins Forum.