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):
| Funktion | Objekt im Block | Ergebnis | Typischer Einsatz |
|---|---|---|---|
let | it | Blockwert | nullable verarbeiten, umwandeln |
run | this | Blockwert | Objekt konfigurieren und etwas berechnen |
with(obj) | this | Blockwert | mehrere Aufrufe auf einem Objekt |
apply | this | das Objekt selbst | Objekt konfigurieren |
also | it | das Objekt selbst | Nebenwirkung (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(mitit),run/with/apply(mitthis) applykonfiguriert Objekte,letverarbeitet Nullbaresrequire,check,runCatching,takeIffür robuste Logik- Lambdas mit Empfänger ermöglichen DSLs
- Ergebnis-Objekte (Sealed Class) als Alternative zu Exceptions
Aufgabe
Schreibe mit apply eine Builder-artige Initialisierung für eine Klasse Server(host, port, debug).