Zum Inhalt springen
Kotlin Anfรคnger 30 min

Variablen, Typen & Null Safety

val und var, Kotlins Typen und das Herzstueck: Null Safety mit ?, !!, ?: und let. Nie wieder NullPointerException.

Aktualisiert:
Inhaltsverzeichnis

Variablen, Typen & Null Safety

Kotlins Typsystem ist streng wie Java, aber wesentlich angenehmer zu nutzen. Der groesste Unterschied: Null Safety.

val und var

val name = "Anna"        // Konstante (kann nicht neu zugewiesen werden)
var alter = 28           // Variable (kann geaendert werden)

alter = 29               // OK
// name = "Max"          // FEHLER - val ist read-only

Faustregel: Nimm val, bis du wirklich var brauchst. Das macht deinen Code vorhersehbarer.

Typ-Inferenz

val name = "Anna"        // String
val alter = 28           // Int
val pi = 3.14            // Double
val aktiv = true         // Boolean

Mit explizitem Typ:

val name: String = "Anna"
val alter: Int = 28
val pi: Double = 3.14

Primitive Typen

Kotlin hat keine primitiven Typen im Java-Sinn - alles sind Objekte. Aber der Compiler optimiert das:

TypBeschreibung
Int32-Bit Ganzzahl
Long64-Bit Ganzzahl
Short16-Bit
Byte8-Bit
Double64-Bit Fliesskomma
Float32-Bit Fliesskomma
Booleantrue / false
CharEinzelnes Unicode-Zeichen
StringZeichenkette

Zahlen-Literale

val i = 42           // Int
val l = 42L          // Long (Suffix L)
val d = 3.14         // Double
val f = 3.14f        // Float (Suffix f)
val hex = 0xFF       // 255
val bin = 0b1010     // 10
val gross = 1_000_000 // Unterstriche als Lesehilfe

Keine impliziten Konvertierungen

val i = 5
val l: Long = i              // FEHLER
val l: Long = i.toLong()     // OK

Das wirkt streng, verhindert aber viele subtile Bugs.

Strings

val name = "Anna"
val laenge = name.length      // 4
val gross = name.uppercase()  // "ANNA"
val ersterBuchstabe = name[0] // 'A'

// Mehrzeilig mit Raw Strings
val sql = """
    SELECT *
    FROM nutzer
    WHERE aktiv = true
""".trimIndent()

Char

val buchstabe: Char = 'A'
val ziffer = '7'

Null Safety

Hier wirdโ€™s spannend - Kotlins groesster Vorteil gegenueber Java.

Standardmaessig nicht-null

var name: String = "Anna"
// name = null             // FEHLER - String kann nicht null sein

Variablen koennen nur null sein, wenn du das explizit zulaesst.

Nullable Types mit ?

var name: String? = "Anna"
name = null                  // OK, weil String? nullable ist

String? ist ein Typ, der entweder einen String enthaelt oder null ist.

Operationen auf Nullables

val name: String? = "Anna"
// val l = name.length       // FEHLER - koennte null sein

Kotlin zwingt dich, mit der null-Moeglichkeit umzugehen:

1. Safe Call ?.

val l: Int? = name?.length   // wenn name null ist, ist l null

2. Elvis-Operator ?:

Liefert einen Default, wenn links null ist:

val l = name?.length ?: 0    // 0 falls name oder length null
val sicherName = name ?: "Anonym"

3. !!-Operator

Behauptet: โ€œDas ist definitiv nicht nullโ€ - wirft NPE, wenn doch:

val l = name!!.length        // crasht wenn name null

Nutze nur, wenn du 100% sicher bist. Meistens ist Elvis oder ?. besser.

4. if-Check

if (name != null) {
    println(name.length)     // Smart Cast - Compiler weiss: nicht null
}

Smart Casts sind elegant: Nach einem != null-Check behandelt Kotlin die Variable automatisch als nicht-null.

5. let mit Safe Call

Ein sehr beliebtes Muster:

name?.let { n ->
    println("Hallo, $n!")
}

let wird nur ausgefuehrt, wenn name nicht null ist.

Type Casting

is und Smart Cast

fun verarbeite(wert: Any) {
    if (wert is String) {
        println("String: ${wert.length}") // Smart Cast zu String
    } else if (wert is Int) {
        println("Int: $wert")
    }
}

as und as?

val obj: Any = "Hallo"

val s1 = obj as String        // wirft Exception wenn nicht String
val s2 = obj as? String       // liefert null wenn nicht String

Collections

Unveraenderbare (bevorzugt)

val namen = listOf("Max", "Anna", "Leo")    // List<String>
val alter = mapOf("Anna" to 28, "Max" to 34) // Map<String, Int>
val farben = setOf("Rot", "Gruen", "Blau")   // Set<String>

// namen.add("Tim")   // FEHLER - read-only

Veraenderbare

val namen = mutableListOf("Max", "Anna")
namen.add("Leo")              // OK
namen.removeAt(0)

val alter = mutableMapOf<String, Int>()
alter["Anna"] = 28

val farben = mutableSetOf<String>()
farben += "Rot"

Arrays

val zahlen = arrayOf(1, 2, 3)
val leer = IntArray(5)        // [0, 0, 0, 0, 0]

Im Alltag nutzt du eher List als Array.

Ranges

val r = 1..10                  // 1 bis 10 inklusiv
val exklusiv = 1 until 10      // 1 bis 9
val schritte = 1..10 step 2    // 1, 3, 5, 7, 9
val runter = 10 downTo 1        // 10, 9, 8, ..., 1

for (i in 1..5) println(i)

Typ-Konvertierung in Strings

val s = "42"
val z = s.toInt()             // wirft Exception bei Fehler
val z2 = s.toIntOrNull()      // Int? - null bei Fehler

val d = "3.14".toDoubleOrNull() ?: 0.0

toIntOrNull / toDoubleOrNull sind in der Praxis fast immer die bessere Wahl.

Zusammenfassung

  • val fuer read-only, var fuer veraenderbar - bevorzuge val
  • Typ-Inferenz oder explizite Typen
  • Null Safety macht null-Moeglichkeit im Typ sichtbar (String?)
  • Safe Call ?., Elvis ?:, Smart Cast, let { ... } sind die Werkzeuge
  • !! nur im Notfall - meistens gibt es besseres
  • listOf, mapOf, setOf fuer unveraenderbar - mutableListOf etc. fuer veraenderbar

Im naechsten Kapitel: Kontrollstrukturen und das beeindruckende when.

Zurรผck zum Kotlin Kurs