EMZETT.
Login

Kurz: Für Klassen, die vor allem Daten halten, erzeugt der Compiler equals, hashCode, toString, copy und Destrukturierung:

Teil des Kurses Kotlin

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

Data Classes

Für Klassen, die vor allem Daten halten, erzeugt der Compiler equals, hashCode, toString, copy und Destrukturierung:

data class Person(val name: String, val alter: Int, val ort: String = "unbekannt")
 
fun main() {
    val a = Person("Mia", 17)
    val b = Person("Mia", 17)
    println(a)
    println(a == b)                  // Inhaltsvergleich
    println(a === b)                 // Identität
    val c = a.copy(alter = 18, ort = "Berlin")
    println(c)
    val (name, alter) = c            // Destrukturierung
    println("$name ist $alter")
    println(setOf(a, b).size)
    println(a.hashCode() == b.hashCode())
}

Ausgabe:

Person(name=Mia, alter=17, ort=unbekannt)
true
false
Person(name=Mia, alter=18, ort=Berlin)
Mia ist 18
1
true

Der Vergleich mit == ruft equals auf (Inhalt). Das ist anders als in Java, wo == Referenzen vergleicht.

Enums

enum class Richtung(val dx: Int, val dy: Int) {
    NORD(0, -1), OST(1, 0), SUED(0, 1), WEST(-1, 0);
 
    fun drehe(): Richtung = values()[(ordinal + 1) % values().size]
}
 
enum class Status { OFFEN, ERLEDIGT }
 
fun text(s: Status) = when (s) {
    Status.OFFEN -> "noch zu tun"
    Status.ERLEDIGT -> "fertig"
}
 
fun main() {
    println(Richtung.NORD.drehe())
    println(Richtung.WEST.drehe())
    println(Richtung.valueOf("SUED").dx)
    println(Richtung.values().map { it.name })
    println(text(Status.OFFEN))
}

Ausgabe:

OST
NORD
0
[NORD, OST, SUED, WEST]
noch zu tun

Sealed Classes: geschlossene Hierarchien

Eine versiegelte Klasse hat eine feste, dem Compiler bekannte Menge von Unterklassen. Dadurch muss when alle Fälle abdecken, ohne else:

sealed class Ergebnis {
    data class Erfolg(val wert: Int) : Ergebnis()
    data class Fehler(val grund: String) : Ergebnis()
    object Laden : Ergebnis()
}
 
fun zeige(e: Ergebnis): String = when (e) {
    is Ergebnis.Erfolg -> "Wert: ${e.wert}"
    is Ergebnis.Fehler -> "Fehler: ${e.grund}"
    Ergebnis.Laden -> "lädt ..."
}
 
fun teile(a: Int, b: Int): Ergebnis =
    if (b == 0) Ergebnis.Fehler("Division durch null") else Ergebnis.Erfolg(a / b)
 
fun main() {
    println(zeige(teile(10, 2)))
    println(zeige(teile(1, 0)))
    println(zeige(Ergebnis.Laden))
}

Ausgabe:

Wert: 5
Fehler: Division durch null
lädt ...

Sealed Classes sind ideal für Zustände (Laden/Erfolg/Fehler in Android-Apps), Ausdrucksbäume und Nachrichtentypen.

Ausdrucksbaum

sealed class Ausdruck
data class Zahl(val n: Double) : Ausdruck()
data class Summe(val l: Ausdruck, val r: Ausdruck) : Ausdruck()
data class Produkt(val l: Ausdruck, val r: Ausdruck) : Ausdruck()
 
fun auswerten(a: Ausdruck): Double = when (a) {
    is Zahl -> a.n
    is Summe -> auswerten(a.l) + auswerten(a.r)
    is Produkt -> auswerten(a.l) * auswerten(a.r)
}
 
fun main() {
    val ausdruck = Produkt(Summe(Zahl(2.0), Zahl(3.0)), Zahl(4.0))
    println(auswerten(ausdruck))
    println(ausdruck)
}

Ausgabe:

20.0
Produkt(l=Summe(l=Zahl(n=2.0), r=Zahl(n=3.0)), r=Zahl(n=4.0))

Typaliase und Pair/Triple

typealias Namen = List<String>
typealias Handler = (String) -> Unit
 
fun begruesse(n: Namen, h: Handler) = n.forEach(h)
 
fun main() {
    begruesse(listOf("Mia", "Tom")) { println("Hallo $it") }
    val p = "a" to 1                         // Pair
    val t = Triple("x", 2, true)
    println("$p ${p.first} ${t.third} ${t.toList()}")
}

Ausgabe:

Hallo Mia
Hallo Tom
(a, 1) a true [x, 2, true]

Merke

  • data class: equals, toString, copy, Destrukturierung inklusive
  • enum class mit Eigenschaften und Methoden; values(), valueOf()
  • sealed class + when ohne else: Der Compiler prüft die Vollständigkeit
  • == vergleicht Inhalte, === Referenzen
  • typealias für lesbare Typnamen

Übungsaufgabe

Modelliere Zahlungsarten (Bar, Karte mit Nummer, PayPal mit E-Mail) als Sealed Class und gib für jede eine Beschreibung aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Klassen und Objekte

Weiter: Interfaces, Generics und Delegation

Alle Kapitel: Kotlin im Überblick