EMZETT.
Login

Kurz: In vielen Sprachen führt null zu Abstürzen (NullPointerException). Kotlin macht Nullbarkeit zum Teil des Typs: String kann niemals null sein, String? schon. Der Compiler erzwingt die Prüfung.

Teil des Kurses Kotlin

Kapitel 5 von 13 im Kurs Kotlin. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Das Milliarden-Dollar-Problem

In vielen Sprachen führt null zu Abstürzen (NullPointerException). Kotlin macht Nullbarkeit zum Teil des Typs: String kann niemals null sein, String? schon. Der Compiler erzwingt die Prüfung.

fun main() {
    val a: String = "Text"
    val b: String? = null
    // val c: String = null        // Fehler: Null kann kein Wert vom Typ String sein
    println(a.length)
    // println(b.length)           // Fehler: nur sicherer Aufruf mit ?. erlaubt
    println(b?.length)             // sicherer Aufruf: null statt Absturz
    println(b ?: "Standard")       // Elvis-Operator: Ersatz bei null
    println(b?.length ?: 0)
}

Ausgabe:

4
null
Standard
0

Der Compiler lehnt null für einen nicht nullbaren Typ ab:

fun main() {
    val c: String = null
}

Fehlermeldung:

main.kt:2:21: error: null can not be a value of a non-null type String

Die Werkzeuge

SyntaxBedeutung
Typ?nullbarer Typ
x?.foo()ruft foo nur auf, wenn x != null, sonst null
x ?: yElvis: x, falls nicht null, sonst y
x!!behauptet “nicht null”, wirft sonst NullPointerException (vermeiden!)
x as? Tsicherer Cast, null bei Fehlschlag
x?.let { ... }Block nur ausführen, wenn nicht null
data class Adresse(val ort: String?)
data class Kunde(val name: String, val adresse: Adresse?)
 
fun ortVon(k: Kunde?): String = k?.adresse?.ort ?: "unbekannt"     // Kette sicherer Aufrufe
 
fun main() {
    println(ortVon(Kunde("Mia", Adresse("Berlin"))))
    println(ortVon(Kunde("Tom", null)))
    println(ortVon(null))
 
    val eingabe: String? = "42"
    eingabe?.let { println("Zahl: ${it.toInt() + 1}") }
 
    val x: Any = "Text"
    val zahl = x as? Int
    println(zahl)
 
    val ergebnis = try { null!! } catch (e: NullPointerException) { "NPE gefangen" }
    println(ergebnis)
}

Ausgabe:

Berlin
unbekannt
unbekannt
Zahl: 43
null
NPE gefangen

Smart Casts

Nach einer Null-Prüfung (oder is-Prüfung) behandelt der Compiler die Variable automatisch als nicht null bzw. als den geprüften Typ:

fun laenge(s: String?): Int {
    if (s == null) return 0
    return s.length                 // hier ist s automatisch String
}
 
fun beschreibe(x: Any): String {
    if (x is String && x.length > 3) return "langer Text: ${x.toUpperCase()}"
    if (x is Int) return "Zahl + 1 = ${x + 1}"
    return "anderes"
}
 
fun main() {
    println(laenge(null))
    println(laenge("Kotlin"))
    println(beschreibe("Hallo"))
    println(beschreibe(41))
    println(beschreibe(2.5))
 
    val liste: List<String?> = listOf("a", null, "b")
    println(liste.filterNotNull())
    println(liste.map { it ?: "-" })
}

Ausgabe:

0
6
langer Text: HALLO
Zahl + 1 = 42
anderes
[a, b]
[a, -, b]

lateinit und lazy

class Dienst {
    lateinit var name: String              // wird später initialisiert (nicht null-Typ)
    val schwer: String by lazy {           // wird erst beim ersten Zugriff berechnet
        println("berechne ...")
        "Ergebnis"
    }
    fun istGesetzt() = ::name.isInitialized
}
 
fun main() {
    val d = Dienst()
    println(d.istGesetzt())
    d.name = "A"
    println(d.istGesetzt())
    println(d.schwer)
    println(d.schwer)
}

Ausgabe:

false
true
berechne ...
Ergebnis
Ergebnis

Merke

  • Typ ist nie null, Typ? kann null sein
  • ?. sicherer Aufruf, ?: Elvis, ?.let { } für Blöcke
  • !! vermeiden: besser prüfen oder ?: nutzen
  • Smart Casts nach == null und is
  • lateinit und by lazy für verzögerte Initialisierung

Übungsaufgabe

Schreibe eine Funktion ersterBuchstabe(s: String?): Char? und gib ”-” aus, wenn nichts vorhanden ist.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Funktionen

Weiter: Listen, Sets, Maps und Collection-Funktionen

Alle Kapitel: Kotlin im Überblick