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.