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())
}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))
}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))
}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)
}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()}")
}Hallo Mia Hallo Tom (a, 1) a true [x, 2, true]
Merke
data class:equals,toString,copy, Destrukturierung inklusiveenum classmit Eigenschaften und Methoden;values(),valueOf()sealed class+whenohneelse: Der Compiler prüft die Vollständigkeit==vergleicht Inhalte,===Referenzentypealiasfür lesbare Typnamen
Aufgabe
Modelliere Zahlungsarten (Bar, Karte mit Nummer, PayPal mit E-Mail) als Sealed Class und gib für jede eine Beschreibung aus.