EMZETT.
Login

Kurz: Fünf Standardfunktionen führen einen Block im Kontext eines Objekts aus. Sie unterscheiden sich in Zugriff (this oder it) und Ergebnis (Objekt oder Blockwert):

Teil des Kurses Kotlin

Kapitel 10 von 13 im Kurs Kotlin (Abschnitt „Fortgeschritten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Scope-Funktionen

Fünf Standardfunktionen führen einen Block im Kontext eines Objekts aus. Sie unterscheiden sich in Zugriff (this oder it) und Ergebnis (Objekt oder Blockwert):

FunktionObjekt im BlockErgebnisTypischer Einsatz
letitBlockwertnullable verarbeiten, umwandeln
runthisBlockwertObjekt konfigurieren und etwas berechnen
with(obj)thisBlockwertmehrere Aufrufe auf einem Objekt
applythisdas Objekt selbstObjekt konfigurieren
alsoitdas Objekt selbstNebenwirkung (Logging)
class Auto { var farbe = ""; var ps = 0; override fun toString() = "$farbe/$ps" }
 
fun main() {
    val auto = Auto().apply { farbe = "rot"; ps = 150 }            // konfigurieren
    println(auto)
 
    val text: String? = "42"
    val zahl = text?.let { it.toInt() * 2 }                       // nur wenn nicht null
    println(zahl)
 
    val laenge = "Kotlin".run { length + 1 }
    println(laenge)
 
    with(auto) { println("Farbe $farbe, $ps PS") }
 
    val liste = mutableListOf(1, 2).also { println("vorher: $it") }.apply { add(3) }
    println(liste)
 
    println(listOf(3, 1, 2).sorted().also { println("sortiert") }.first())
}

Ausgabe:

rot/150
84
7
Farbe rot, 150 PS
vorher: [1, 2]
[1, 2, 3]
sortiert
1

Weitere nützliche Helfer

fun main() {
    val x = 5
    check(x > 0) { "x muss positiv sein" }            // IllegalStateException bei Verstoß
    require(x < 10)                                   // IllegalArgumentException
    val wert = x.takeIf { it > 3 } ?: -1
    val nicht = x.takeUnless { it > 3 }
    println("$wert $nicht")
    repeat(2) { print("hi ") }
    println()
 
    val ergebnis = runCatching { "abc".toInt() }
    println(ergebnis.isFailure)
    println(ergebnis.getOrDefault(0))
    println(runCatching { "12".toInt() }.getOrNull())
 
    val (a, b) = 10 to 3
    println("${a / b} ${a % b}")
 
    println(TODO_KOMMENTAR)
}
 
const val TODO_KOMMENTAR = "TODO() wirft NotImplementedError bei Aufruf"

Ausgabe:

5 null
hi hi
true
0
12
3 1
TODO() wirft NotImplementedError bei Aufruf

Eigene DSLs bauen

Mit Lambdas mit Empfänger (T.() -> Unit) entstehen lesbare Mini-Sprachen, wie sie Gradle oder Compose nutzen:

class Html {
    private val teile = mutableListOf<String>()
    fun h1(text: String) { teile.add("<h1>$text</h1>") }
    fun p(text: String) { teile.add("<p>$text</p>") }
    fun ul(block: Liste.() -> Unit) {
        val l = Liste().apply(block)
        teile.add("<ul>" + l.punkte.joinToString("") { "<li>$it</li>" } + "</ul>")
    }
    override fun toString() = teile.joinToString("\n")
}
 
class Liste {
    val punkte = mutableListOf<String>()
    fun punkt(text: String) { punkte.add(text) }
}
 
fun html(block: Html.() -> Unit): Html = Html().apply(block)
 
fun main() {
    val seite = html {
        h1("Titel")
        p("Ein Absatz")
        ul {
            punkt("eins")
            punkt("zwei")
        }
    }
    println(seite)
}

Ausgabe:

<h1>Titel</h1>
<p>Ein Absatz</p>
<ul><li>eins</li><li>zwei</li></ul>

Pattern: Ergebnis statt Exception

Statt Exceptions für erwartbare Fehler zu werfen, gibst du ein Ergebnis-Objekt zurück. Mit einer generischen Sealed Class baust du das selbst (neuere Kotlin-Versionen bieten zusätzlich kotlin.Result als Rückgabetyp):

sealed class Resultat<out T> {
    data class Ok<T>(val wert: T) : Resultat<T>()
    data class Fehler(val meldung: String) : Resultat<Nothing>()
}
 
fun teilen(a: Int, b: Int): Resultat<Int> =
    if (b == 0) Resultat.Fehler("Division durch null") else Resultat.Ok(a / b)
 
fun anzeigen(r: Resultat<Int>) = when (r) {
    is Resultat.Ok -> "Wert ${r.wert}"
    is Resultat.Fehler -> "Fehler: ${r.meldung}"
}
 
fun main() {
    println(anzeigen(teilen(10, 2)))
    println(anzeigen(teilen(1, 0)))
}

Ausgabe:

Wert 5
Fehler: Division durch null

Merke

  • Scope-Funktionen: let/also (mit it), run/with/apply (mit this)
  • apply konfiguriert Objekte, let verarbeitet Nullbares
  • require, check, runCatching, takeIf für robuste Logik
  • Lambdas mit Empfänger ermöglichen DSLs
  • Ergebnis-Objekte (Sealed Class) als Alternative zu Exceptions

Übungsaufgabe

Schreibe mit apply eine Builder-artige Initialisierung für eine Klasse Server(host, port, debug).

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Interfaces, Generics und Delegation

Weiter: Fehler, Dateien und Java-Interop

Alle Kapitel: Kotlin im Überblick