Ketika sebuah class hanya dipakai sebagai helper oleh class lain, kita sering ingin membungkusnya di dalam class pemakai agar struktur kode lebih rapi. Kotlin mendukung nested class — class yang dideklarasikan di dalam class lain — secara default tanpa keyword tambahan. Nested class bersifat statis terhadap outer class, artinya nested class tidak membawa referensi ke instance outer dan tidak dapat membaca property anggota outer. Pada chapter ini kita akan mendeklarasikan nested class, mengamati keterbatasan aksesnya, dan melihat tiga pola pemakaian umum: helper operasi, pengelompokan konstanta, dan builder pattern.
Apa Itu Nested Class #
Nested class adalah class yang dideklarasikan di dalam body class lain. Karena tidak memakai keyword inner, nested class tidak memiliki akses ke instance outer class — seolah-olah class tersebut bersifat statis. Untuk membuat instance nested class, kita memakai sintaks Outer.Nested() tanpa harus memiliki instance Outer terlebih dahulu.
Karakteristik ini berbeda dari Java, di mana class bersarang secara default adalah inner class yang membawa referensi implisit ke instance outer. Kotlin memilih kebalikannya: class bersarang default-nya statis (mirip static class di Java), dan kita harus menulis inner class secara eksplisit jika ingin membawa referensi outer.
Deklarasi dan Pemakaian #
Contoh berikut mendefinisikan class MesinKalkulator yang berisi nested class Operasi. Class Operasi tidak membawa referensi instance MesinKalkulator dan dapat dipakai tanpa membuat instance MesinKalkulator lebih dulu. Contoh juga menunjukkan nested object untuk konstanta dan nested class builder untuk membangun object Laporan bertahap.
// nested_classes.kt
// Mendemonstrasikan nested class (tanpa keyword `inner`).
class MesinKalkulator {
private val faktorSkala = 100
// Nested class statis: tidak membawa referensi ke instance MesinKalkulator.
class Operasi {
fun tambah(a: Int, b: Int): Int = a + b
fun kurang(a: Int, b: Int): Int = a - b
fun kali(a: Int, b: Int): Int = a * b
}
fun hitungDenganFaktor(a: Int, b: Int): Int {
val op = Operasi()
// Meskipun berada di dalam MesinKalkulator, instance Operasi tidak
// dapat melihat property `faktorSkala` milik MesinKalkulator.
return op.tambah(a, b) * 0 + op.tambah(a, b)
}
}
// Nested class untuk pengelompokan konstanta dan helper.
class KonfigurasiApp {
object Kunci {
const val NAMA = "belajar-kode"
const val VERSI = "1.0"
}
class Pemverifikasi {
fun periksaNama(nama: String): Boolean {
return nama.matches(Regex("^[a-z0-9-]+$"))
}
}
}
// Nested class sebagai builder untuk object kompleks.
class Laporan(val judul: String, val isi: String, val penulis: String) {
class Builder {
private var judul: String = ""
private var isi: String = ""
private var penulis: String = ""
fun judul(value: String) = apply { judul = value }
fun isi(value: String) = apply { isi = value }
fun penulis(value: String) = apply { penulis = value }
fun build(): Laporan = Laporan(judul, isi, penulis)
}
}
fun main() {
// Memakai nested class tanpa instance outer.
val op = MesinKalkulator.Operasi()
println("Operasi tambah 7 + 5 = ${op.tambah(7, 5)}")
println("Operasi kurang 10 - 4 = ${op.kurang(10, 4)}")
println("Operasi kali 6 * 3 = ${op.kali(6, 3)}")
println()
val calc = MesinKalkulator()
println("Hasil MesinKalkulator.hitungDenganFaktor(3, 5) = ${calc.hitungDenganFaktor(3, 5)}")
println()
println("KonfigurasiApp.Kunci.NAMA = ${KonfigurasiApp.Kunci.NAMA}")
println("KonfigurasiApp.Kunci.VERSI = ${KonfigurasiApp.Kunci.VERSI}")
val ver = KonfigurasiApp.Pemverifikasi()
println("Periksa 'belajar-kode': ${ver.periksaNama("belajar-kode")}")
println("Periksa 'Nama!@#': ${ver.periksaNama("Nama!@#")}")
println()
val laporan = Laporan.Builder()
.judul("Laporan Tahunan")
.isi("Penjualan naik 20%.")
.penulis("Budi")
.build()
println("Laporan -> judul: ${laporan.judul}, penulis: ${laporan.penulis}")
}Output yang Dihasilkan:
Operasi tambah 7 + 5 = 12
Operasi kurang 10 - 4 = 6
Operasi kali 6 * 3 = 18
Hasil MesinKalkulator.hitungDenganFaktor(3, 5) = 8
KonfigurasiApp.Kunci.NAMA = belajar-kode
KonfigurasiApp.Kunci.VERSI = 1.0
Periksa 'belajar-kode': true
Periksa 'Nama!@#': false
Laporan -> judul: Laporan Tahunan, penulis: BudiMari kita bedah kode di atas:
class Operasi { fun tambah(...); fun kurang(...); fun kali(...) }: nested class statis di dalamMesinKalkulator. Tidak ada keywordinner, sehinggaOperasitidak membawa referensi ke instanceMesinKalkulator.val op = MesinKalkulator.Operasi(): instanceOperasidibuat langsung lewat sintaksOuter.Nested(). Tidak perlu membuat instanceMesinKalkulatorterlebih dahulu.private val faktorSkala = 100: property instance milikMesinKalkulator. ClassOperasitidak dapat membaca property ini karenaOperasibersifat statis terhadap outer.object Kunci { const val NAMA = "belajar-kode" }: nestedobjectmenyimpan konstanta. Karenaconsthanya boleh berada di tingkat top-level atau dalamobject, nested object adalah cara alami untuk mengelompokkan konstanta yang berhubungan dengan satu class.class Pemverifikasi { fun periksaNama(...) }: nested class helper yang memverifikasi format nama. Dideklarasikan bersamaKuncikarena keduanya menjadi bagian dariKonfigurasiApp.class Builder { fun judul(...) = apply { ... }; fun build(): Laporan }: pola builder menggunakan nested class. Method mengembalikanBuilder(viaapply) sehingga pemanggilan dapat dirantai seperti.judul(...).isi(...).penulis(...).build().
Kapan Memakai Nested Class #
Nested class cocok ketika sebuah class bersifat helper untuk class lain dan tidak memerlukan akses ke instance outer. Pola umum yang sering ditemui:
- Helper murni: class yang berisi static utility functions yang spesifik untuk satu class.
- Pengelompokan konstanta: nested
objectuntuk konstanta yang berhubungan dengan class luar. - Builder pattern: class builder yang menyimpan state sementara sebelum membangun object target.
- Implementasi internal: class pribadi yang hanya dipakai di dalam outer class dan tidak perlu diekspos ke luar.
Batasan Akses #
Nested class tidak dapat membaca property instance outer secara langsung. Kode berikut tidak akan dikompilasi:
class Outer {
private val pesan = "halo"
class Nested {
fun cetak() {
// println(pesan) // Error: tidak bisa akses property instance Outer
}
}
}Bila kita benar-benar membutuhkan akses ke instance outer, gunakan keyword inner (akan dibahas pada chapter Inner Classes). Nested class tetap dapat mengakses companion object atau nested object milik outer, karena keduanya bersifat statis terhadap outer.
Referensi Lanjutan #
- Nested classes — panduan resmi nested class yang tidak membawa referensi outer
Ringkasan #
Nested class adalah class bersarang yang bersifat statis terhadap outer — tidak membawa referensi ke instance outer dan dapat dipakai tanpa instance outer. Nested class cocok untuk helper, pengelompokan konstanta, dan builder pattern. Bila helper membutuhkan akses ke state instance outer, gunakan inner class yang akan kita bahas pada chapter berikutnya.