Funktional

Lambda-Methoden in Kotlin

Kotlin ist primär eine funktionale Sprache. Das bedeutet, dass Methoden oder Codeblöcke wie Parameter übergeben und zu einem späteren Zeitpunkt ausgeführt werden können.

Unter Java war dieses Verhalten lange Zeit nur durch Verwendung von Vererbung mit anonymen inneren Klassen reproduzierbar:

button.setOnClickListener(new OnClickListener {
        @Override void onClick(View view) {
            // click action
    }
}

Mit Java 8 kam dann die Möglichkeit von Lambdas hinzu:

button.setOnClickListener(view -> { /* click action */ }

Und unter Kotlin sieht das ganze so aus:

button.setOnClickListener { /* click action */ }

Kotlin verfügt u.a. über eine Reihe von zusätzlichen Befehlen, die auf dieser Funktionalität beruhen: with, apply, also, run und let. Diese sind untereinander in ihrer Anwendung sehr ähnlich, wir werden uns hier also ein paar Beispiele ansehen.

with(object) { … }

Für den Fall, daß man nacheinander mehrere Methoden oder Parameter auf demselben Objekt aufrufen muß, lassen sich diese alle mit with zusammenfassen. Somit entfällt im Code die ständige Wiederholung der Objektreferenz, sofern man die Aufrufe nicht miteinander verketten konnte. (Siehe dazu auch die Konzepte Flow-API oder Builder-Pattern).

// Java
Coordinates coord = getCoordinates();
setLon(coord.x);
setLat(coord.y);
setElev(coord.z);
setTime(coord.t);
// Kotlin
with(getCoordinates()) {
    setLon(x); setLat(y); setElev(z); setTime(t) 
}

run { … } und .run { … }

Für den Fall, dass das Objekt null sein kann, müsste im with-Block jedem Aufruf this?. vorangestellt werden. Statt dessen kann man das äquivalente Konstrukt mit run verwenden:

getCoordinates()?.run {
    setLon(x); setLat(y); setElev(z); setTime(t)
}

Die eigenständige Funktion run { } ohne vorhergehendes Objekt kann man dann verwenden, um einen eigenen Scope zu erzeugen, in dem die Anweisungen laufen sollen:

run { if (first) headerView else contentView }.show()

.apply { … }

Diese Funktion wird auf dem Objekt selbst aufgerufen. Das bietet sich unter anderem an, wenn nach der Instanziierung noch eine Initialisierung erfolgen muß, und weder ein entsprechender Konstruktor noch eine Flow-API vorhanden sind:

// Java
Coordinates coord = new Coordinates();
coord.x = getLon();
coord.y = getLat();
coord.z = getElev();
coord.t = getTime();
coord.validate();
// Kotlin
coord = Coordinates().apply {
    x = getLat(); y = getLat(); z = getElev(); t = getTime()
    validate()
}

Mehrere apply – Aufrufe lassen sich außerdem miteinander verketten, die jeweils das Objekt als this referenziert durch die Kette weiterreichen.

.also { … } und .let { … }

Der Unterschied dieser beiden Funktionen zu apply ist, daß hier für das Objekt eine neue Referenz it verwendet wird, sofern kein anderer Name angegeben wird. Während let am Ende der Funktion ein neues Objekt weiterreichen kann, gibt also hier immer das Ursprungsobjekt zurück. Der Ausdruck

password.also { encode(it) }.also { print(it) }

zeigt demnach den ursprünglichen, unverschlüsselten String an, während

password.let { encode(it) }.let { print(it) }

den verarbeiteten und verschlüsselten Text anzeigt.