Zum Inhalt springen
Kotlin Anfรคnger 30 min

Classes, Data Classes & Objects

OOP auf Kotlin-Art: kompakte Klassen mit primaeren Konstruktoren, magische Data Classes und Objects als Singletons.

Aktualisiert:
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 erzeugt
  • var 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 werden
  • open fun geraeusch - darf ueberschrieben werden
  • override 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)
  • toString
  • copy(...) fuer neue Instanzen mit Aenderungen
  • componentN()-Funktionen fuer Destrukturierung

Ideal fuer DTOs, Modelle, unveraenderliche Datencontainer.

Visibility Modifiers

ModifierScope
publicueberall (Default in Kotlin)
privatenur in der Datei / Klasse
protectedKlasse + Subklassen (nicht fuer Top-Level)
internalim 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/var im Konstruktor erzeugt direkt Properties
  • Klassen sind final by default - open fuer 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.

Zurรผck zum Kotlin Kurs