NullPointerException adalah galat yang paling sering muncul pada ekosistem JVM dan biasanya baru terdeteksi saat program sudah berjalan. Kotlin mengatasinya dengan membagi tipe referensi menjadi dua kategori sejak compile-time: tipe non-nullable seperti String yang tidak boleh menyimpan null, dan tipe nullable seperti String? yang diizinkan menyimpan null. Pembedaan ini dilakukan langsung oleh kompilator sehingga banyak potensi galat dapat ditangkap sebelum program dijalankan. Pada chapter ini kita akan membedah perbedaan kedua kategori tersebut, cara mendeklarasikannya, dan implikasinya terhadap keamanan kode.
Perbedaan Tipe Non-Nullable dan Nullable #
Tipe non-nullable adalah perilaku bawaan Kotlin untuk semua tipe referensi. Saat kita menulis val nama: String = "Budi", kompilator menjamin variabel nama tidak akan pernah berisi null. Setiap upaya menetapkan null ke nama akan ditolak saat kompilasi. Sebaliknya, tipe nullable ditandai dengan tanda tanya di akhir tipe: String?. Tipe ini mengizinkan nilai null sebagai nilai yang sah.
Berikut ringkasan perbedaan kedua kategori:
| Aspek | String (non-nullable) | String? (nullable) |
|---|---|---|
Boleh null? | Tidak, compile error | Ya |
Akses .length langsung? | Aman, tidak butuh pengecekan | Tidak aman, perlu ?. atau cek != null |
| Tujuan | Data wajib ada | Data opsional atau belum pasti |
| Risiko NPE saat runtime | Hampir nol jika tidak ada interop Java | Perlu ditangani eksplisit |
Deklarasi dan Penggunaan Dasar #
Mari kita amati contoh konkret yang memperlihatkan kedua kategori tipe berdampingan:
fun main() {
val namaNonNullable: String = "Budi"
println("Nama non-nullable: $namaNonNullable")
println("Panjang nama: ${namaNonNullable.length}")
val namaNullable: String? = null
println("Nama nullable: $namaNullable")
val namaNullableBerisi: String? = "Siti"
println("Nama nullable berisi: $namaNullableBerisi")
println("Panjang (pakai safe call): ${namaNullableBerisi?.length}")
println("Panjang (dari null): ${namaNullable?.length}")
val daftar: List<String?> = listOf("Budi", null, "Dewi")
println("Daftar dengan elemen null: $daftar")
for (item in daftar) {
println("Item: ${item ?: "<kosong>"}")
}
val tipeNonNullable: String = "Halo"
val tipeNullable: String? = tipeNonNullable
println("Konversi non-nullable ke nullable: $tipeNullable")
val umur: Int = 25
val umurNullable: Int? = umur
println("Umur non-nullable: $umur, umur nullable: $umurNullable")
val idPengguna: Long? = null
val idDefault: Long = idPengguna ?: 0L
println("ID pengguna (default 0): $idDefault")
}Output yang Dihasilkan:
Nama non-nullable: Budi
Panjang nama: 4
Nama nullable: null
Nama nullable berisi: Siti
Panjang (pakai safe call): 4
Panjang (dari null): null
Daftar dengan elemen null: [Budi, null, Dewi]
Item: Budi
Item: <kosong>
Item: Dewi
Konversi non-nullable ke nullable: Halo
Umur non-nullable: 25, umur nullable: 25
ID pengguna (default 0): 0Mari kita uraikan baris-baris penting pada contoh di atas:
- Baris 2 (
val namaNonNullable: String = "Budi"): tipeStringtanpa?berarti variabel ini tidak boleh menyimpannull. Kompilator menjamin hal tersebut sejak compile-time. - Baris 5 (
val namaNullable: String? = null): tanda?setelahStringmengubah tipe menjadi nullable. Sekarangnulladalah nilai yang sah. - Baris 9 (
namaNullableBerisi?.length): operator safe call?.hanya memanggil.lengthbilanamaNullableBerisitidak null; bila null, hasil seluruh ekspresi menjadinull. Tanpa?., akses.lengthlangsung pada tipe nullable akan ditolak kompilator. - Baris 13 (
List<String?>): tipe elemen list juga dapat nullable. List ini berisi kombinasiStringdannull. - Baris 16 (
item ?: "<kosong>"): operator Elvis?:memberi nilai default saatitemnull. Topik ini akan kita bahas tuntas pada chapter Elvis Operator. - Baris 19 (
val tipeNullable: String? = tipeNonNullable): nilai non-nullable dapat ditugaskan ke variabel nullable tanpa konversi eksplisit karena tipe nullable adalah supertype dari tipe non-nullable. Arah sebaliknya tidak aman dan perlu pengecekan eksplisit.
Mengapa Pembedaan Ini Penting #
Pada bahasa seperti Java, semua referensi secara teknis bisa null. Setiap pemanggilan method seperti user.getName().length() berpotensi melempar NullPointerException bila salah satu titik di rantai tersebut ternyata null. Kotlin memindahkan kekhawatiran tersebut ke compile-time: jika kita ingin sebuah variabel bisa null, kita harus menyatakannya dengan eksplisit menggunakan ?, dan pemanggilan method pada variabel nullable wajib menggunakan operator ?. atau diawali dengan pengecekan != null.
Referensi Lanjutan #
- Null-safety
— panduan resmi perbedaan tipe nullable
String?dan non-nullableString
Dengan pemahaman perbedaan kedua kategori tipe, langkah berikutnya adalah membahas cara mendeklarasikan variabel nullable dan mengubah nilainya sepanjang program — materi yang akan kita bahas pada chapter Nullable Variables.