Deklarasi variabel kadang tidak selalu beriringan dengan inisialisasi langsung. Pada beberapa skenario, kita perlu menunda pemberian nilai hingga kondisi tertentu terpenuhi, atau menunda inisialisasi hingga variabel benar-benar diakses. Kotlin menyediakan beberapa pola deklarasi: inisialisasi langsung dengan tipe eksplisit, pola declare first, assign later, modifier lateinit var, serta delegasi lazy. Pada chapter ini kita akan membedah keempat pola tersebut beserta kapan masing-masing cocok dipakai.

Deklarasi dengan tipe eksplisit #

Bentuk paling sederhana deklarasi variabel adalah val nama: Tipe = nilai. Tipe ditulis setelah nama variabel dengan tanda titik dua. Tipe eksplisit berguna ketika kita ingin membatasi tipe yang lebih spesifik daripada inferensi kompilator.

kotlin Deklarasi dengan tipe eksplisit
val nama: String = "Budi"
val umur: Int = 25
val tinggi: Double = 170.5

Pola ini cocok ketika nilai literal ambigu atau ketika kita ingin mendokumentasikan tipe secara jelas.

Declare first, assign later #

Kotlin memperbolehkan deklarasi val tanpa inisialisasi asalkan variabel tersebut dijamin diberi nilai tepat sekali sebelum digunakan. Pola ini umum dipakai pada cabang if/when:

kotlin Pola declare first, assign later
val namaPengguna: String
if (tahun >= 2026) {
    namaPengguna = "Budi"
} else {
    namaPengguna = "Tamu"
}
println(namaPengguna)  // Budi

Kompilator memverifikasi bahwa semua cabang menetapkan nilai ke namaPengguna sebelum digunakan. Bila ada cabang yang tidak menetapkan nilai, kompilasi akan gagal.

lateinit var #

Modifier lateinit var memungkinkan deklarasi variabel non-nullable tanpa nilai awal, dengan janji bahwa nilai akan disematkan sebelum variabel diakses. Pola ini cocok untuk dependency injection, framework testing, atau properti yang diisi oleh kode eksternal.

kotlin lateinit var
lateinit var pesanSambutan: String

fun main() {
    pesanSambutan = "Halo, Budi!"
    println(pesanSambutan)
}

Syarat lateinit:

  • Hanya untuk var, bukan val.
  • Hanya untuk tipe non-primitif (tidak untuk Int, Double, Boolean).
  • Hanya untuk tipe non-nullable.
  • Akses sebelum diinisialisasi memunculkan UninitializedPropertyAccessException.

lazy init #

Delegasi lazy menunda inisialisasi hingga pertama kali variabel diakses, lalu meng-cache hasilnya untuk akses berikutnya. lazy cocok untuk objek yang mahal dibuat atau yang mungkin tidak selalu dibutuhkan saat program berjalan.

kotlin lazy [ ]
val konfigurasiDefault: String by lazy {
    println(">> Menghitung konfigurasi default...")
    "mode=produksi;port=8080"
}

Blok lazy dijalankan tepat sekali, pada akses pertama. Akses kedua dan seterusnya mengembalikan nilai yang di-cache, tanpa menjalankan blok lagi. lazy bersifat thread-safe secara default (mode LazyThreadSafetyMode.SYNCHRONIZED).

Contoh lengkap #

Berikut program yang memadukan keempat pola:

kotlin
lateinit var pesanSambutan: String

val konfigurasiDefault: String by lazy {
    println(">> Menghitung konfigurasi default...")
    "mode=produksi;port=8080"
}

fun main() {
    val tahun: Int = 2026
    println("Tahun: $tahun")

    val namaPengguna: String
    if (tahun >= 2026) {
        namaPengguna = "Budi"
    } else {
        namaPengguna = "Tamu"
    }
    println("Nama pengguna: $namaPengguna")

    pesanSambutan = "Selamat datang di Kotlin, $namaPengguna!"
    println(pesanSambutan)

    println("Sebelum akses konfigurasi")
    println("Konfigurasi: $konfigurasiDefault")
    println("Konfigurasi (lagi): $konfigurasiDefault")
}

Output yang Dihasilkan:

text
Tahun: 2026
Nama pengguna: Budi
Selamat datang di Kotlin, Budi!
Sebelum akses konfigurasi
>> Menghitung konfigurasi default...
Konfigurasi: mode=produksi;port=8080
Konfigurasi (lagi): mode=produksi;port=8080

Mari kita amati baris-baris penting pada kode di atas:

  1. Baris 1 (lateinit var pesanSambutan) — deklarasi var tanpa inisialisasi. Kompilator mengizinkan ini karena modifier lateinit memberi janji eksplisit.
  2. Baris 3-6 (val konfigurasiDefault by lazy { ... }) — blok lambda akan dieksekusi sekali pada akses pertama. Output ">> Menghitung konfigurasi default..." muncul sekali, meskipun variabel diakses dua kali.
  3. Baris 10 (val tahun: Int = 2026) — deklarasi dengan tipe eksplisit dan inisialisasi langsung.
  4. Baris 13-18 (val namaPengguna: String; if (...) ... else ...) — pola declare first, assign later. Cabang if/else menjamin namaPengguna selalu mendapat nilai.
  5. Baris 21 (pesanSambutan = "...") — inisialisasi lateinit var sebelum diakses pada baris 22.
  6. Baris 24-26 (println("Konfigurasi: $konfigurasiDefault") x2) — lazy hanya menjalankan blok pada akses pertama; akses kedua mengembalikan nilai yang sama tanpa menjalankan blok.

Memilih pola yang tepat #

PolaKapan dipakaiKapan dihindari
Tipe eksplisit + inisialisasiNilai sudah ada sejak awal—
Declare first, assign laterNilai bergantung cabang if/whenHanya satu cabang
lateinit varDependency injection, framework testingTipe primitif atau nullable
lazy initObjek mahal atau jarang dipakaiObjek ringan yang selalu dipakai

Referensi Lanjutan #

  • Basic types — pengantar tipe dasar Kotlin untuk pola deklarasi dan inisialisasi variabel
  • Properties — dokumentasi properti Kotlin yang menjelaskan lateinit, by lazy, dan backing field

Pemilihan pola deklarasi yang tepat membuat program lebih aman dan efisien. Setelah memahami pola deklarasi, kita siap melihat bagaimana kompilator Kotlin menentukan tipe variabel tanpa kita menyebutkannya — materi pada chapter Type Inference.