Sebuah singleton adalah class yang hanya boleh memiliki satu instance selama aplikasi berjalan. Pola ini sering dipakai untuk konfigurasi global, pabrik object, registry, dan koneksi terpusat. Di Java, singleton biasanya ditulis dengan kombinasi private constructor, field static INSTANCE, dan method getInstance() yang mengurus inisialisasi thread-safe. Kotlin menyederhanakan pola tersebut menjadi satu keyword object. Pada chapter ini kita akan mendeklarasikan singleton dengan object, melihat inisialisasi lazy yang dilakukan otomatis, dan memakai object sebagai factory yang menghasilkan instance class lain.
Apa Itu object Declaration #
Declaration object Nama { ... } di Kotlin mendefinisikan sekaligus membuat instance tunggal dari sebuah class anonim. Kompilator menjamin hanya ada satu instance selama aplikasi berjalan, dan instance tersebut dibuat secara lazy — instance hanya diinisialisasi ketika pertama kali diakses. Setelah dibuat, instance tersebut di-share ke seluruh kode yang menyebut nama object tersebut.
Akses ke anggota object langsung lewat nama object — tidak perlu membuat instance dengan Nama() karena instance sudah otomatis ada. Penulisan mirip dengan memanggil static member di Java, namun di Kotlin yang dipanggil adalah method pada instance singleton, bukan method statis.
Singleton Sederhana #
Contoh berikut mendefinisikan tiga object: Konfigurasi untuk konstanta aplikasi, PabrikPengguna sebagai factory yang menghasilkan instance Pengguna, dan PenyanggaLog dengan init block yang berjalan sekali saat singleton pertama kali diakses.
// object_declarations.kt
// Mendemonstrasikan `object` declaration sebagai singleton di Kotlin.
object Konfigurasi {
const val NAMA_APLIKASI = "BelajarKode"
const val VERSI = "1.0"
// State singleton — di-share ke seluruh aplikasi.
var jumlahPermintaan = 0
private set
fun catatPermintaan() {
jumlahPermintaan++
println("Permintaan #$jumlahPermintaan diproses oleh $NAMA_APLIKASI v$VERSI")
}
fun ringkasan(): String = "$NAMA_APLIKASI v$VERSI — $jumlahPermintaan permintaan"
}
object PabrikPengguna {
private var urutan = 1000
fun buat(nama: String): Pengguna {
urutan++
return Pengguna(urutan, nama)
}
fun daftarDefault(): List<Pengguna> = listOf(
buat("Budi"),
buat("Siti"),
buat("Dewi"),
)
}
data class Pengguna(val id: Int, val nama: String)
// Singleton dengan init block — kode dijalankan sekali saat akses pertama.
object PenyanggaLog {
private val baris = mutableListOf<String>()
init {
// Inisialisasi sekali saat instance singleton dibuat.
baris.add("[boot] singleton PenyanggaLog siap")
}
fun catat(pesan: String) {
baris.add(pesan)
}
fun ambilSemua(): List<String> = baris.toList()
}
fun main() {
// Akses singleton langsung lewat nama `object`.
println(Konfigurasi.NAMA_APLIKASI)
println(Konfigurasi.VERSI)
println()
Konfigurasi.catatPermintaan()
Konfigurasi.catatPermintaan()
Konfigurasi.catatPermintaan()
println(Konfigurasi.ringkasan())
println()
val p1 = PabrikPengguna.buat("Andi")
val p2 = PabrikPengguna.buat("Maya")
println("Pengguna baru: ${p1.nama} (id=${p1.id})")
println("Pengguna baru: ${p2.nama} (id=${p2.id})")
println("Daftar default: ${PabrikPengguna.daftarDefault().map { it.nama }}")
println()
PenyanggaLog.catat("permintaan diproses")
PenyanggaLog.catat("keluar aplikasi")
println("Isi PenyanggaLog:")
for (baris in PenyanggaLog.ambilSemua()) {
println(" - $baris")
}
}Output yang Dihasilkan:
BelajarKode
1.0
Permintaan #1 diproses oleh BelajarKode v1.0
Permintaan #2 diproses oleh BelajarKode v1.0
Permintaan #3 diproses oleh BelajarKode v1.0
BelajarKode v1.0 — 3 permintaan
Pengguna baru: Andi (id=1001)
Pengguna baru: Maya (id=1002)
Daftar default: [Budi, Siti, Dewi]
Isi PenyanggaLog:
- [boot] singleton PenyanggaLog siap
- permintaan diproses
- keluar aplikasiMari kita bedah kode di atas:
object Konfigurasi { const val NAMA_APLIKASI = ...; var jumlahPermintaan = 0 }: deklarasiobjectsekaligus membuat instance tunggal. KonstantaNAMA_APLIKASIdanVERSImemakaiconst valkarena nilainya literal yang dapat diinline saat kompilasi.var jumlahPermintaan = 0; private set: state singleton yang dapat dibaca dari luar namun hanya dapat diubah dari dalam. Setiap pemanggilancatatPermintaan()menambah nilai ini — karena instance tunggal, perubahan terlihat oleh semua kode yang mengaksesKonfigurasi.jumlahPermintaan.Konfigurasi.catatPermintaan(): akses method singleton langsung lewat namaobject. Tidak ada sintaksKonfigurasi.getInstance()seperti pola Java klasik.object PabrikPengguna { private var urutan = 1000; fun buat(...): Pengguna }:objectsebagai factory yang menjaga state urutan ID. Setiap pemanggilanbuat()menambahurutandan mengembalikan instancePenggunabaru dengan ID berurutan.Pengguna(id=1001), Pengguna(id=1002): bukti stateurutandipertahankan antar pemanggilan — instancePabrikPenggunatetap sama, sehingga counter tidak di-reset.init { baris.add("[boot] ...") }di dalamobject:initblock padaobjectdijalankan sekali saat instance singleton pertama kali dibuat (lazy). Karena output menampilkan baris[boot]lebih dulu daripada dua baris berikutnya, terlihat bahwainitberjalan sebelum methodcatatdipanggil.PenyanggaLog.ambilSemua(): mengembalikan salinan list sebagaiList<String>— pola defensif agar pemanggil tidak dapat memodifikasi list internal singleton secara langsung.
Inisialisasi Lazy #
Kompilator Kotlin menghasilkan kode yang menjamin inisialisasi singleton bersifat thread-safe dan lazy. Instance object hanya dibuat saat pertama kali diakses, bukan saat aplikasi dimulai. Untuk PenyanggaLog, instance baru dibuat ketika PenyanggaLog.catat(...) pertama dipanggil — init block berjalan tepat saat itu juga, sebelum method catat dieksekusi.
Sifat lazy berguna ketika inisialisasi singleton mahal (misalnya membaca file konfigurasi atau membuka koneksi database). Singleton yang tidak pernah diakses tidak akan mengonsumsi resource.
object sebagai Factory dan Registry #
Pemakaian umum object selain konfigurasi global adalah factory dan registry. PabrikPengguna di atas adalah contoh factory: pemanggil tidak peduli bagaimana instance Pengguna dibuat, mereka hanya memanggil PabrikPengguna.buat(nama) dan menerima instance siap pakai dengan ID yang dijamin unik oleh state internal factory. Bila kita mengubah algoritma pembuatan ID (misalnya menambahkan prefix), hanya satu tempat yang perlu diubah.
Referensi Lanjutan #
- Object declarations
— panduan resmi singleton via keyword
object
Ringkasan #
Deklarasi object membuat singleton secara langsung dengan instance tunggal dan inisialisasi lazy yang thread-safe. Akses anggota object langsung lewat nama tanpa membuat instance. object cocok untuk konfigurasi global, factory, dan registry yang membutuhkan satu instance bersama. Pada chapter companion Objects berikutnya, kita akan melihat bagaimana object dapat diletakkan di dalam class untuk menyediakan anggota statis.