Classes, Data Classes & Objects
OOP auf Kotlin-Art: kompakte Klassen mit primaeren Konstruktoren, magische Data Classes und Objects als Singletons.
Inhaltsverzeichnis
Classes, Data Classes & Objects
Kotlins Klassen sind wesentlich kompakter als Java-Klassen. Und data class und object bringen Features, die es in Java so nicht gibt.
Eine einfache Klasse
class Person(val name: String, var alter: Int) {
fun vorstellen() {
println("Hallo, ich bin $name und $alter Jahre alt.")
}
}
fun main() {
val anna = Person("Anna", 28)
anna.vorstellen()
anna.alter = 29
}
Der primary constructor steht direkt in der Klassendeklaration - das ist fast die gesamte Klasse.
val vs. var im Konstruktor
val name- liest nur, Property wird automatisch erzeugtvar alter- kann geaendert werden- Ohne
val/var- nur Parameter, wird kein Property
Init-Block
Wenn du im Konstruktor mehr machen willst:
class Person(val name: String, var alter: Int) {
init {
require(alter >= 0) { "Alter kann nicht negativ sein" }
println("Person $name angelegt")
}
}
Sekundaere Konstruktoren
class Person(val name: String, var alter: Int) {
constructor(name: String) : this(name, 0)
}
val a = Person("Anna", 28)
val b = Person("Max") // alter = 0
Properties
Properties in Kotlin sind meist Auto-Properties, aber du kannst auch Getter und Setter kontrollieren:
class Rechteck(val breite: Double, val hoehe: Double) {
val flaeche: Double
get() = breite * hoehe
var durchmesser: Double = 0.0
get() = Math.sqrt(breite * breite + hoehe * hoehe)
private set
}
Vererbung
Kotlin-Klassen sind standardmaessig final. Wenn du ableiten willst, muss die Basisklasse open sein:
open class Tier(val name: String) {
open fun geraeusch(): String = "..."
}
class Hund(name: String) : Tier(name) {
override fun geraeusch(): String = "Wuff!"
}
class Katze(name: String) : Tier(name) {
override fun geraeusch(): String = "Miau!"
}
open class Tier- darf vererbt werdenopen fun geraeusch- darf ueberschrieben werdenoverride fun geraeusch- ueberschreibt
Abstract Classes
abstract class Form {
abstract fun flaeche(): Double
fun info() {
println("Flaeche: ${flaeche()}")
}
}
class Kreis(val radius: Double) : Form() {
override fun flaeche() = Math.PI * radius * radius
}
Interfaces
interface Fliegend {
fun fliegen()
fun maxHoehe(): Int = 1000 // Default-Implementierung erlaubt
}
interface Schwimmend {
fun schwimmen()
}
class Ente : Fliegend, Schwimmend {
override fun fliegen() = println("Flatter")
override fun schwimmen() = println("Platsch")
}
Kotlin unterstuetzt mehrere Interfaces, aber nur eine Basisklasse.
Data Classes - das Highlight
data class macht aus deiner Klasse einen vollstaendigen Datentyp:
data class Person(val name: String, val alter: Int)
fun main() {
val a = Person("Anna", 28)
val b = Person("Anna", 28)
println(a) // Person(name=Anna, alter=28)
println(a == b) // true - wertbasierter Vergleich
println(a.hashCode()) // konsistent mit ==
val c = a.copy(alter = 29) // Kopie mit Aenderung
println(c) // Person(name=Anna, alter=29)
// Destrukturierung
val (name, alter) = a
println("$name, $alter")
}
data class generiert:
equals/hashCode(wertbasiert, nicht referenzbasiert)toStringcopy(...)fuer neue Instanzen mit AenderungencomponentN()-Funktionen fuer Destrukturierung
Ideal fuer DTOs, Modelle, unveraenderliche Datencontainer.
Visibility Modifiers
| Modifier | Scope |
|---|---|
public | ueberall (Default in Kotlin) |
private | nur in der Datei / Klasse |
protected | Klasse + Subklassen (nicht fuer Top-Level) |
internal | im gleichen Modul |
class Konto(private var saldo: Double) {
fun einzahlen(betrag: Double) {
require(betrag > 0)
saldo += betrag
}
fun saldo() = saldo
}
Objects - Singletons
object erstellt ein Single-Instance-Objekt:
object Konfiguration {
val version = "1.0.0"
var debug = false
fun hello() = println("Willkommen")
}
fun main() {
println(Konfiguration.version)
Konfiguration.hello()
Konfiguration.debug = true
}
Kein new, keine Instanz - einfach nur der Name.
Companion Object
Fuer โstatischeโ Member einer Klasse:
class Person(val name: String) {
companion object {
fun anonym() = Person("Anonym")
const val MAX_NAME = 50
}
}
val a = Person.anonym() // wie static Method
val max = Person.MAX_NAME
Sealed Classes
Ein โgeschlossenerโ Typ - alle Subklassen sind bekannt. Perfekt mit when:
sealed class Ergebnis {
data class Erfolg(val wert: String) : Ergebnis()
data class Fehler(val fehler: String) : Ergebnis()
object Leer : Ergebnis()
}
fun verarbeite(r: Ergebnis) = when (r) {
is Ergebnis.Erfolg -> "Ok: ${r.wert}"
is Ergebnis.Fehler -> "Fehler: ${r.fehler}"
is Ergebnis.Leer -> "Nichts da"
}
// kein else noetig - Compiler weiss, dass alle Faelle abgedeckt sind
Praktisches Beispiel
data class Produkt(val name: String, val preis: Double, val kategorie: String)
class Warenkorb {
private val artikel = mutableListOf<Produkt>()
fun hinzufuegen(p: Produkt) {
artikel.add(p)
}
val gesamt: Double
get() = artikel.sumOf { it.preis }
fun ausPro(kat: String) = artikel.filter { it.kategorie == kat }
override fun toString() =
"Warenkorb mit ${artikel.size} Artikeln fuer ${gesamt}"
}
fun main() {
val korb = Warenkorb()
korb.hinzufuegen(Produkt("Buch", 19.99, "Medien"))
korb.hinzufuegen(Produkt("Tasse", 8.00, "Haushalt"))
korb.hinzufuegen(Produkt("CD", 12.99, "Medien"))
println(korb)
println(korb.ausPro("Medien").map { it.name })
}
Zusammenfassung
- Klassen sind kompakt: Primaerer Konstruktor in der Deklaration
val/varim Konstruktor erzeugt direkt Properties- Klassen sind final by default -
openfuer Vererbung - Data Classes generieren
equals,hashCode,toString,copy, Destrukturierung - Objects sind Singletons, Companion Objects ersetzen static
- Sealed Classes sind exhaustive Types fuer
when
Damit hast du die Werkzeuge, um idiomatischen Kotlin-Code zu schreiben. Im weiteren Kurs vertiefen wir Generics, Delegation, Extensions und Coroutines.