Ce face data class
Multe clase există doar ca să țină date: un produs, un elev, o comandă. Marcând clasa cu data, compilatorul îi scrie automat funcțiile utile, pe baza proprietăților din constructorul primar: toString() (afișare clară, de exemplu Elev(nume=Ana, nota=10)), equals()/hashCode() (două obiecte cu aceleași valori sunt egale), copy() și component1(), component2()… pentru destructuring.
copy creează un obiect nou, cu unele proprietăți schimbate: elev.copy(nota = 9). Așa lucrezi comod cu obiecte imutabile (doar val). Egalitatea pe valori face ca obiectele data să meargă corect în Set și ca chei în Map.
Reguli: constructorul primar trebuie să aibă cel puțin un parametru, toți val sau var. Proprietățile declarate în corpul clasei nu intră în toString/equals. O data class nu poate fi open sau abstract. Din Kotlin 1.9 există și data object, un obiect unic cu toString frumos.
Exemple
Exemplul 1: toString, equals și copy
Două obiecte cu aceleași valori sunt egale (==), deși sunt obiecte diferite (===). copy schimbă doar nota.
data class Elev(val nume: String, val clasa: String, val nota: Int)
fun main() {
val ana = Elev("Ana", "a X-a B", 9)
println(ana)
val altaAna = Elev("Ana", "a X-a B", 9)
println("Egale ca valori: ${ana == altaAna}")
println("Același obiect: ${ana === altaAna}")
val dupaContestatie = ana.copy(nota = 10)
println(dupaContestatie)
println("Originalul nu s-a schimbat: $ana")
}
Rezultat
Elev(nume=Ana, clasa=a X-a B, nota=9)
Egale ca valori: true
Același obiect: false
Elev(nume=Ana, clasa=a X-a B, nota=10)
Originalul nu s-a schimbat: Elev(nume=Ana, clasa=a X-a B, nota=9)
Exemplul 2: În Set, ca chei în Map și cu destructuring
Datorită lui equals/hashCode, adresele duplicate dispar din Set. val (oras, strada) = ... folosește component1 și component2.
data class Adresa(val oras: String, val strada: String)
fun main() {
val livrari = listOf(
Adresa("Iași", "Lăpușneanu 10"),
Adresa("Cluj", "Memorandumului 2"),
Adresa("Iași", "Lăpușneanu 10"),
)
val unice = livrari.toSet()
println("${livrari.size} livrări, ${unice.size} adrese unice")
val colete = mapOf(Adresa("Cluj", "Memorandumului 2") to 3)
println("Colete pentru Cluj: ${colete[Adresa("Cluj", "Memorandumului 2")]}")
val (oras, strada) = unice.first()
println("Prima adresă: $strada, $oras")
}
Rezultat
3 livrări, 2 adrese unice
Colete pentru Cluj: 3
Prima adresă: Lăpușneanu 10, Iași
Exemplul 3: Ce intră în funcțiile generate
stoc este declarat în corp, deci nu apare în toString și nu contează la ==. data object se afișează cu numele lui.
data class Produs(val cod: String, val pret: Int) {
var stoc: Int = 0
}
data object CosGol
fun main() {
val a = Produs("P-1", 25).apply { stoc = 10 }
val b = Produs("P-1", 25).apply { stoc = 0 }
println(a)
println("a == b: ${a == b} (stocul nu contează)")
println(CosGol)
}
Rezultat
Produs(cod=P-1, pret=25)
a == b: true (stocul nu contează)
CosGol
Greșeli frecvente
- Proprietăți
varîntr-odata classfolosită ca cheie înHashMapsau înHashSet: dacă modifici obiectul după ce l-ai pus,hashCodese schimbă și nu-l mai găsești. Preferăvalșicopy. - Așteptarea ca
copysă copieze în adâncime: listele din interior sunt aceleași obiecte în copie și în original. data classfără parametri (data class Gol()): nu este permis. Foloseștedata object.
Comenzi înrudite
class: class declară o clasă: un tip nou care grupează date (proprietăți) și comportament (metode).Destructuring (val (a, b) = ...): Destructuring desface un obiect în mai multe variabile deodată: val (nume, nota) = pereche.value class: value class învelește o singură valoare într-un tip nou, pentru siguranță la compilare, fără costul unui obiect la rulare.sealed class și sealed interface: sealed declară o ierarhie închisă: toate subtipurile sunt cunoscute la compilare, deci when le poate verifica pe toate fără else.object: object declară o clasă cu un singur obiect (singleton) sau creează pe loc un obiect anonim care implementează o interfață.