data class in Kotlin – copy(), Destrukturierung und warum ich sie ständig nutze
Ein `data class` ist mehr als ein Container für Felder. Du bekommst equals(), hashCode(), toString(), copy() und Destrukturierung geschenkt – und arbeitest damit sauber mit unveränderlichen Objekten. Ich zeige dir die Teile, die im Alltag wirklich zählen.
In fast jeder App, die ich baue, gibt es kleine Datenhalter: ein Nutzerprofil, ein Warenkorb-Eintrag, ein UI-Zustand. Dafür schreibe ich fast nie eine normale Klasse, sondern eine data class – die schenkt mir jede Menge Boilerplate, den ich sonst von Hand tippen (und pflegen) müsste.
Was du geschenkt bekommst
data class User(val name: String, val age: Int)
val a = User("Mia", 30)
val b = User("Mia", 30)
println(a) // User(name=Mia, age=30) ← lesbares toString()
println(a == b) // true ← equals() vergleicht die Werte
println(a.hashCode() == b.hashCode()) // true ← passend zu equals()Bei einer normalen Klasse wäre a == b false (Referenzvergleich). Eine data class vergleicht dagegen inhaltlich – genau das, was man bei Daten fast immer will. Deshalb funktionieren sie auch als Set-Elemente oder Map-Schlüssel korrekt.
copy() – ändern, ohne das Original anzufassen
Der Teil, den ich am häufigsten brauche: Ich halte meine Objekte unveränderlich (val) und erzeuge bei einer Änderung eine neue Kopie mit copy().
val user = User("Mia", 30)
val geburtstag = user.copy(age = 31) // nur age wird ersetzt
println(user) // User(name=Mia, age=30) ← unverändert
println(geburtstag) // User(name=Mia, age=31)Das ist die Grundlage für sauberes State-Management (etwa in einem ViewModel mit Compose): Der alte Zustand bleibt, du produzierst einen neuen. Kein versehentliches Mutieren von Objekten, die woanders noch benutzt werden.
Destrukturierung – Felder direkt entpacken
val (name, age) = user
println("$name ist $age") // Mia ist 30Praktisch in Schleifen über Paare oder Map-Einträge:
for ((id, punkte) in scores) {
println("Spieler $id hat $punkte Punkte")
}Die Reihenfolge zählt: Destrukturierung geht nach der Position der Konstruktor-Parameter, nicht nach dem Namen. Ordnest du die Parameter um, ändert sich also auch das Entpacken.
Eine Warnung zum Schluss
equals(), copy() & Co. betrachten nur die Properties im primären Konstruktor. Ein Feld, das du erst im Klassenrumpf deklarierst (val extra = …), fließt nicht in equals()/copy() ein. Wenn ein Wert zur Identität gehört, muss er in den Konstruktor.
Wenn du eine Android-App in Kotlin planst und Wert auf sauberen, wartbaren Code legst, setze ich das gern für dich um – schreib mir über bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
kotlinx.serialization: JSON und data class ohne Handarbeit verbinden
Eine API antwortet mit JSON, deine App will eine data class. Dazwischen steht in vielen Projekten ein handgeschriebener Parser. Mit kotlinx.serialization sind es eine Annotation und eine Zeile – und ein Schalter, ohne den deine App beim nächsten API-Update abstürzt.
AnimatedVisibility in Jetpack Compose – ein- und ausblenden, das nicht springt
Ein Element einfach mit if wegzulassen, poppt hart weg. AnimatedVisibility blendet es weich ein und aus – mit sinnvollen Voreinstellungen und, wenn du willst, deiner eigenen Kombination aus Faden und Bewegung. Ich zeige dir beides.
snapshotFlow in Jetpack Compose – aus Compose-Zustand einen Flow machen
Manchmal willst du auf einen Compose-Zustand reagieren wie auf einen Datenstrom – entprellen, kombinieren, sammeln. Genau das macht snapshotFlow. Ich zeige dir das an der häufigsten Aufgabe: mehr laden, wenn der Nutzer ans Listenende scrollt.