Banyak class yang kita tulis hanyalah wadah data — sekumpulan property dengan method equals, hashCode, dan toString yang membosankan untuk ditulis ulang di setiap class. Kotlin menyediakan keyword data pada deklarasi class yang membuat kompilator otomatis menghasilkan method tersebut berdasarkan property yang dideklarasikan di primary constructor. Cukup tulis data class Pengguna(val nama: String, val umur: Int), dan kita mendapatkan class lengkap dengan perbandingan struktur, hash konsisten, cetakan string yang informatif, method copy untuk modifikasi sebagian, dan method componentN untuk destrukturisasi. Pada chapter ini kita akan mendeklarasikan beberapa data class dan mengamati kelima method yang dihasilkan kompilator.
Apa Itu data class #
data class adalah class yang dideklarasikan dengan keyword data sebelum class. Kompilator Kotlin menghasilkan method berikut secara otomatis berdasarkan property di primary constructor:
equals(other: Any?): membandingkan dua instance secara struktural — true bila seluruh property sama.hashCode(): nilai hash yang konsisten denganequals— dua instance yang equal menghasilkan hashCode yang sama.toString(): format"NamaClass(prop1=val1, prop2=val2)"yang mudah dibaca.copy(...): membuat salinan instance dengan opsi mengubah sebagian property.component1(),component2(), dst.: mendukung destrukturisasival (a, b) = instance.
Syarat utama data class: harus memiliki primary constructor dengan minimal satu property (dengan val atau var). Constructor dapat memiliki default value, dan class tetap dapat menambah method biasa di body.
Deklarasi Dasar #
Contoh berikut mendefinisikan empat data class — Pengguna, Buku, Titik, dan ItemKeranjang — yang mendemonstrasikan method auto-generated: equals, hashCode, toString, copy, dan componentN.
// data_classes.kt
// Mendemonstrasikan `data class` Kotlin — auto-generates equals/hashCode/
// toString/copy/componentN.
data class Pengguna(val nama: String, val umur: Int)
data class Buku(
val judul: String,
val penulis: String,
val tahun: Int,
val kategori: String = "Umum",
)
// Data class dengan custom equals — auto-generated tetap dipakai bila tidak
// di-override. Di sini kita menambahkan method biasa untuk demonstrasi.
data class Titik(val x: Int, val y: Int) {
// Method tambahan tetap dapat ditambahkan ke data class.
fun jarakKe(lain: Titik): Double {
val dx = (lain.x - x).toDouble()
val dy = (lain.y - y).toDouble()
return Math.sqrt(dx * dx + dy * dy)
}
}
// Data class untuk struktur ringkas, mirip record di Java.
data class ItemKeranjang(val produk: String, val harga: Int, val jumlah: Int) {
fun total(): Int = harga * jumlah
}
fun main() {
val p1 = Pengguna("Budi", 25)
val p2 = Pengguna("Budi", 25)
val p3 = Pengguna("Siti", 30)
println("p1 = $p1")
println("p2 = $p2")
println("p1 == p2 ? ${p1 == p2}") // structural equals
println("p1 === p2 ? ${p1 === p2}") // referential
println("p1 == p3 ? ${p1 == p3}")
println()
// hashCode konsisten dengan equals.
println("hashCode p1 = ${p1.hashCode()}")
println("hashCode p2 = ${p2.hashCode()}")
println("hashCode p1 == hashCode p2 ? ${p1.hashCode() == p2.hashCode()}")
println()
// copy dengan modifikasi sebagian field.
val p4 = p1.copy(umur = 26)
println("p1.copy(umur = 26) = $p4")
println("p1 setelah copy tetap = $p1")
println()
// Destructuring via componentN.
val (nama, umur) = p1
println("Destructure: nama=$nama, umur=$umur")
println("p1.component1()=${p1.component1()}, p1.component2()=${p1.component2()}")
println()
val buku1 = Buku("Belajar Kotlin", "Budi", 2026)
val buku2 = Buku("Belajar Kotlin", "Budi", 2026, "Pemrograman")
println("buku1 = $buku1")
println("buku2 = $buku2")
println("buku1 == buku2 ? ${buku1 == buku2}")
println()
val a = Titik(0, 0)
val b = Titik(3, 4)
println("a = $a, b = $b")
println("jarak a -> b = ${a.jarakKe(b)}")
println()
val keranjang = listOf(
ItemKeranjang("Buku", 50_000, 2),
ItemKeranjang("Pulpen", 5_000, 5),
ItemKeranjang("Map", 3_000, 4),
)
val totalBelanja = keranjang.sumOf { it.total() }
println("Daftar belanja:")
for (item in keranjang) {
println(" - ${item.produk}: ${item.harga} x ${item.jumlah} = ${item.total()}")
}
println("Total belanja = $totalBelanja")
}Output yang Dihasilkan:
p1 = Pengguna(nama=Budi, umur=25)
p2 = Pengguna(nama=Budi, umur=25)
p1 == p2 ? true
p1 === p2 ? false
p1 == p3 ? false
hashCode p1 = 64537313
hashCode p2 = 64537313
hashCode p1 == hashCode p2 ? true
p1.copy(umur = 26) = Pengguna(nama=Budi, umur=26)
p1 setelah copy tetap = Pengguna(nama=Budi, umur=25)
Destructure: nama=Budi, umur=25
p1.component1()=Budi, p1.component2()=25
buku1 = Buku(judul=Belajar Kotlin, penulis=Budi, tahun=2026, kategori=Umum)
buku2 = Buku(judul=Belajar Kotlin, penulis=Budi, tahun=2026, kategori=Pemrograman)
buku1 == buku2 ? false
a = Titik(x=0, y=0), b = Titik(x=3, y=4)
jarak a -> b = 5.0
Daftar belanja:
- Buku: 50000 x 2 = 100000
- Pulpen: 5000 x 5 = 25000
- Map: 3000 x 4 = 12000
Total belanja = 137000Mari kita bedah kode di atas:
data class Pengguna(val nama: String, val umur: Int): deklarasi satu baris. Kompilator otomatis menghasilkan kelima method (equals,hashCode,toString,copy,componentN) berdasarkan dua property ini. Padanannya di Java membutuhkan sekitar 40 baris boilerplate.p1 == p2 ? true: operator==di Kotlin memanggilequals(). Karenap1danp2memilikinamadanumuryang sama, hasilnyatrue. Bandingkan denganp1 === p2yang menghasilkanfalsekarena===membandingkan referensi memori, danp1/p2adalah instance yang berbeda.p1.hashCode() == p2.hashCode() ? true: kontrakhashCodemensyaratkan dua instance yangequalsjuga memiliki hashCode yang sama. Data class memenuhi kontrak ini secara otomatis — penting saat instance dipakai sebagai kunci diHashMapatau elemenHashSet.p1.copy(umur = 26): membuat salinanp1denganumurdiganti26. Property yang tidak disebut (nama) tetap membawa nilai darip1. Pola ini memudahkan pembuatan instance baru yang hampir sama dengan instance lama, tanpa mengubah instance asli.val (nama, umur) = p1: destrukturisasi. Sintaks ini memanggilp1.component1()untuk mengisinamadanp1.component2()untuk mengisiumur. Kedua methodcomponentNini dihasilkan otomatis untuk setiap property di primary constructor.data class Buku(..., val kategori: String = "Umum"): property dengan default value. Saat membuat instance, pemanggil dapat mengosongkankategoriuntuk memakai default, atau menyediakan nilai eksplisit. Perhatikanbuku1 == buku2menghasilkanfalsekarenakategoriberbeda ("Umum"vs"Pemrograman") — semua property diperhitungkan dalamequals.data class Titik(...) { fun jarakKe(lain: Titik): Double { ... } }: data class tetap dapat menambah method biasa di body. MethodjarakKemenghitung jarak Euclidean antara dua titik — method ini bukan bagian dari property sehingga tidak memengaruhiequalsatauhashCode.ItemKeranjang("Buku", 50_000, 2)denganfun total(): Int = harga * jumlah: data class dengan method bantu. Pola umum untuk model domain — property menyimpan state, method menghitung turunan._di literal50_000adalah pemisah visual yang memudahkan pembacaan angka besar.
Method yang Dihasilkan #
Tabel berikut merangkum method yang dihasilkan otomatis untuk data class Pengguna(val nama: String, val umur: Int):
| Method | Yang dihasilkan kompilator | Pemakaian |
|---|---|---|
equals | Bandingkan nama dan umur | p1 == p2 |
hashCode | Hash dari nama dan umur | set.add(p1) |
toString | "Pengguna(nama=Budi, umur=25)" | println(p1) |
copy | Salinan dengan override sebagian | p1.copy(umur = 26) |
component1 | Mengembalikan nama | val (n, _) = p1 |
component2 | Mengembalikan umur | val (_, u) = p1 |
Batasan data class #
Data class memiliki beberapa batasan: tidak boleh di-open (tidak dapat diwariskan secara default, kecuali sejak Kotlin 1.1 dapat diwariskan dengan syarat ketat), tidak boleh abstract atau sealed, dan tidak boleh mewarisi data class lain. Untuk class yang memerlukan inheritance, gunakan class biasa dan tulis method equals/hashCode secara manual bila diperlukan. Untuk model murni yang hanya menyimpan data, data class adalah pilihan terbaik.
Referensi Lanjutan #
- Data classes
— panduan resmi
data classdan method yang dihasilkan otomatis
Ringkasan #
data class menghasilkan method equals, hashCode, toString, copy, dan componentN secara otomatis berdasarkan property di primary constructor. Satu baris deklarasi menggantikan puluhan baris boilerplate Java. Data class tetap dapat memiliki method biasa di body dan property dengan default value. Pada chapter enum class berikutnya, kita akan melihat class khusus untuk merepresentasikan sekumpulan nilai konstan.