Setelah memahami perbedaan kategori tipe nullable dan non-nullable, kita perlu tahu cara mendeklarasikan variabel nullable dan mengelola nilainya sepanjang eksekusi program. Variabel nullable dapat dibuat dengan val (read-only) maupun var (mutable), dan setiap perubahan nilainya tetap diawasi oleh kompilator. Pada chapter ini kita akan menulis berbagai bentuk deklarasi variabel nullable, mengamati perilakunya saat diisi dengan nilai maupun null, serta melihat bagaimana kompilator menolak penetapan null pada tipe non-nullable.

Deklarasi Variabel Nullable #

Cara paling langsung mendeklarasikan variabel nullable adalah dengan menambahkan ? pada tipe yang disebutkan. Sintaks ini berlaku untuk semua tipe referensi seperti String, Int, Long, maupun tipe buatan sendiri.

kotlin
fun main() {
    val nama: String = "Budi"
    println("Nama non-nullable: $nama")

    var namaOpsional: String? = null
    println("NamaOpsional mula-mula: $namaOpsional")
    namaOpsional = "Siti"
    println("NamaOpsional setelah diisi: $namaOpsional")
    namaOpsional = null
    println("NamaOpsional setelah di-null-kan: $namaOpsional")

    val umur: Int? = null
    println("Umur nullable: $umur")

    val harga: Double? = 12_500.0
    println("Harga nullable: $harga, pakai safe call: ${harga?.toInt()}")

    val data: List<String?> = listOf("A", null, "B")
    println("List berisi null: $data")

    val idPengguna: Long? = null
    val id: Long = idPengguna ?: 0L
    println("ID default dari nullable: $id")

    val panjang: Int? = namaOpsional?.length
    println("Panjang dari namaOpsional: $panjang")

    var status: String? = "aktif"
    val panjangStatus = status?.length
    println("Status=$status, panjang=$panjangStatus")
    status = null
    val panjangStatusSekarang = status?.length
    println("Status=$status, panjang=$panjangStatusSekarang")

    val angkaHeksadesimal: Int? = 0xFF
    println("Angka hex nullable: $angkaHeksadesimal")

    val elemenPertama: String? = data.first()
    println("Elemen pertama dari data: $elemenPertama")
}

Output yang Dihasilkan:

text
Nama non-nullable: Budi
NamaOpsional mula-mula: null
NamaOpsional setelah diisi: Siti
NamaOpsional setelah di-null-kan: null
Umur nullable: null
Harga nullable: 12500.0, pakai safe call: 12500
List berisi null: [A, null, B]
ID default dari nullable: 0
Panjang dari namaOpsional: null
Status=aktif, panjang=5
Status=null, panjang=null
Angka hex nullable: 255
Elemen pertama dari data: A

Mari kita uraikan baris-baris penting pada contoh di atas:

  1. Baris 2 (val nama: String = "Budi"): variabel non-nullable dengan val. Variabel ini tidak dapat diubah nilainya maupun dijadikan null.
  2. Baris 5 (var namaOpsional: String? = null): variabel mutable nullable. Kata kunci var memungkinkan penetapan ulang, termasuk kembali ke null seperti terlihat pada baris 8.
  3. Baris 10 (val umur: Int? = null): variabel read-only nullable. val mencegah penetapan ulang, namun tetap mengizinkan nilai awal berupa null.
  4. Baris 13 (harga?.toInt()): variabel nullable yang berisi nilai tetap perlu safe call ?. untuk mengakses method-nya. Hasilnya juga bertipe nullable (Int?).
  5. Baris 16 (List<String?>): tipe elemen list juga dapat nullable. Hal ini umum saat membaca data dari sumber eksternal yang mungkin mengandung elemen kosong.
  6. Baris 19 (idPengguna ?: 0L): operator Elvis memberi nilai default 0L bila idPengguna null. Hasilnya bertipe non-nullable Long sehingga aman dipakai pada operasi aritmetika.
  7. Baris 22 (namaOpsional?.length): setiap kali kita menetapkan ulang nilai namaOpsional (misalnya ke null di baris 8), kompilator tetap mengetahui tipenya tetap String? sehingga akses method tetap wajib memakai ?. atau pengecekan eksplisit.
  8. Baris 32 (val elemenPertama: String? = data.first()): function first() pada List<String?> mengembalikan String? karena elemen pertama list bisa jadi adalah null. Tipe kembalian mengikuti tipe elemen.

Galat Compile-time pada Penetapan Null #

Kompilator Kotlin akan menolak kode yang mencoba menetapkan null ke tipe non-nullable. Perhatikan contoh berikut yang sengaja dibuat salah:

kotlin Contoh: penetapan null pada tipe non-nullable (compile error)
val nama: String = null

Saat dikompilasi, kotlinc akan mengeluarkan pesan galat seperti berikut:

text
error: null can not be a value of a non-null type String
val nama: String = null
                   ^

Pesan tersebut menjelaskan secara eksplisit bahwa tipe String tidak menerima nilai null. Untuk membuat variabel yang dapat menyimpan null, kita harus menggunakan tipe nullable String?.

Pertimbangan Memilih val atau var untuk Variabel Nullable #

Pilihan antara val dan var pada variabel nullable memengaruhi cara kode dirawat. Variabel val dengan tipe nullable cocok untuk nilai yang hanya dibaca sekali namun mungkin tidak tersedia saat deklarasi — misalnya hasil pencarian database yang bisa jadi tidak ditemukan. Variabel var dengan tipe nullable cocok untuk state yang berubah sepanjang siklus hidup objek, namun setiap akses tetap perlu penanganan eksplisit karena nilai bisa jadi null di tengah jalan.

Salah satu kesalahan umum adalah memakai var x: T? = null sebagai pengganti inisialisasi tertunda. Pola ini memaksa setiap akses berikutnya memakai ?. atau pengecekan != null, padahal kita tahu nilai akan diisi sebelum pertama kali dipakai. Pendekatan yang lebih aman adalah memakai lateinit var (untuk tipe non-nullable seperti String dan Int) atau by lazy (untuk inisialisasi sekali pakai). Topik ini akan dibahas tuntas pada chapter Null Safety Best Practices.

Referensi Lanjutan #

  • Null-safety — panduan resmi mendeklarasikan variabel nullable dan menugaskan null

Dengan kemampuan mendeklarasikan variabel nullable, langkah berikutnya adalah memanggil method pada variabel tersebut dengan aman. Materi tentang safe call operator ?. akan kita bahas pada chapter berikutnya.