Nilai bertipe nullable tidak dapat dipakai begitu saja, karena compiler menolak setiap pemanggilan anggota yang tidak disertai penanganan null. Kotlin menyediakan dua operator utama untuk keperluan itu, yaitu safe call yang ditulis ?. dan elvis operator yang ditulis ?:. Topik ini membahas bentuk penolakan compiler, cara kerja safe call termasuk pemakaiannya secara berantai, serta penggunaan elvis operator untuk nilai bawaan dan untuk menghentikan program.
Compiler menuntut penanganan null#
Sebuah variabel String? tidak boleh memanggil .length secara langsung. Aturan ini terasa ketat pada awalnya, tetapi pesan errornya menyebutkan dua pintu penanganan yang disediakan Kotlin. Berkas berikut memuat pemanggilan semacam itu agar bentuk penolakannya terlihat.
fun main() {
val catatan: String? = "Belanja mingguan"
println(catatan.length)
}Dua baris export pada perintah berikut mengarahkan terminal ke compiler Kotlin yang dipakai jalur ini, dan jalurnya menyesuaikan dengan lokasi instalasi pada komputer lain.
# compiler Kotlin dan pustaka standar yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi berkas salah.kt untuk melihat pesan error dari compiler
kotlinc salah.kt -d salah.jarsalah.kt:3:20: error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'String?'.
println(catatan.length)
^Pesan error only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver menyebut kedua pilihan yang sah, yaitu safe call dan non-null assertion. Perhatikan bahwa nilai catatan pada berkas di atas sebenarnya terisi, tetapi compiler menilai dari tipenya, bukan dari isinya. Penilaian yang konsisten seperti ini memungkinkan compiler memeriksa seluruh program tanpa menelusuri alur nilai satu per satu. Niat penanganan pun wajib tertulis pada setiap pemanggilan, sehingga keputusan tentang nilai kosong tidak hilang di antara baris-baris kode.
Ketegasan ini berbeda dengan bahasa yang membiarkan pemanggilan pada nilai kosong lolos dari kompilasi, sehingga pesan errornya baru muncul ketika program sedang berjalan. Pada Kotlin, keputusan tentang null ditagih pada waktu kompilasi, dan dua operator berikutnya menampung berbagai bentuk keputusan tersebut.
Safe call dengan ?.#
Safe call memanggil anggota hanya apabila nilai di sebelah kirinya terisi, dan menghasilkan null apabila nilai tersebut kosong. Operator ini menggantikan tanda titik biasa, sehingga penulisannya tetap ringkas. Program berikut memakai safe call pada dua nilai, satu yang terisi dan satu yang kosong.
fun main() {
val alamatAli: String? = "Jalan Merdeka 10"
val alamatBudi: String? = null
println("Alamat Ali: ${alamatAli?.uppercase()}")
println("Alamat Budi: ${alamatBudi?.uppercase()}")
println("Panjang alamat Ali: ${alamatAli?.uppercase()?.length}")
println("Panjang alamat Budi: ${alamatBudi?.uppercase()?.length}")
}# kompilasi berkas main.kt menjadi out.jar, lalu jalankan programnya
kotlinc main.kt -d out.jar && java -cp "out.jar:$STDLIB" MainKtAlamat Ali: JALAN MERDEKA 10
Alamat Budi: null
Panjang alamat Ali: 16
Panjang alamat Budi: nullPada alamat Ali, pemanggilan uppercase() dijalankan sehingga teksnya berubah menjadi huruf kapital. Pada alamat Budi, pemanggilan yang sama melewatkan pekerjaannya dan menghasilkan null, sehingga baris keduanya mencetak null tanpa error. Rantai ?.uppercase()?.length memperlihatkan sifat penting safe call, yaitu null pada satu mata rantai membuat seluruh rantai menghasilkan null. Rantai panjang seperti ini biasanya muncul ketika data tersusun berlapis, dan safe call menjaga rantainya tetap aman tanpa pemeriksaan bertingkat.
Pola lain yang lazim dipadukan dengan safe call adalah ?.let, yaitu menjalankan blok kode hanya bila nilai terisi.
// tidak-dijalankan
catatan?.let {
println("Panjang: ${it.length}")
}Blok di dalam let hanya dijalankan ketika catatan terisi, dan nilai itu tersedia di dalam blok dengan nama it. Pembahasan lengkap mengenai let, termasuk kaitannya dengan pengembalian awal, tersedia pada topik let dan Pengembalian Awal di dalam modul ini.
Elvis operator untuk nilai bawaan#
Safe call melubangkan hasilnya, karena String?.uppercase() bertipe String? dan ?.length bertipe Int?. Elvis operator menutup lubang tersebut dengan menyediakan nilai pengganti apabila sisi kirinya bernilai null. Bentuknya a ?: b, yang dibaca sebagai nilai a bila terisi, atau b bila kosong. Sisi kanannya hanya dievaluasi ketika sisi kiri bernilai null. Program berikut memakai elvis operator pada dua bentuk pemakaian.
fun main() {
val statistik: Int? = null
val rataRata = statistik ?: 0
println("Rata-rata sementara: $rataRata")
val teks: String? = "24"
val angka = teks?.toInt() ?: 0
println("Angka: $angka")
}# kompilasi berkas elvis.kt menjadi elvis.jar, lalu jalankan programnya
kotlinc elvis.kt -d elvis.jar && java -cp "elvis.jar:$STDLIB" ElvisKtRata-rata sementara: 0
Angka: 24Variabel statistik bernilai null, sehingga elvis operator mengambil nilai bawaan 0 dan rataRata menjadi Int yang pasti terisi. Pada baris kedua, safe call dan elvis operator bekerja berpasangan, yaitu teks?.toInt() bertipe Int? dan sisi kanan ?: 0 mengisi kekosongannya. Hasil akhirnya aman dipakai tanpa pemeriksaan tambahan. Pasangan ?. dan ?: seperti ini merupakan pola bawaan untuk nilai yang mungkin kosong, karena keduanya melengkapi kekurangan satu sama lain.
Elvis operator dengan throw#
Sisi kanan elvis operator tidak harus berupa nilai, tetapi boleh berupa throw. Bentuk ini dipakai ketika kekosongan dianggap keadaan yang tidak dapat dilanjutkan, sehingga program berhenti dengan pesan yang jelas alih-alih berjalan dengan data yang salah.
fun main() {
val idTugas: Int? = null
val id = idTugas ?: throw IllegalArgumentException("idTugas wajib diisi")
println("ID: $id")
}# kompilasi berkas lempar.kt, lalu jalankan dengan keluaran dibatasi tiga baris pertama
kotlinc lempar.kt -d lempar.jar && java -cp "lempar.jar:$STDLIB" LemparKt 2>&1 | head -3Exception in thread "main" java.lang.IllegalArgumentException: idTugas wajib diisi
at LemparKt.main(lempar.kt:3)
at LemparKt.main(lempar.kt)Program terhenti pada baris ketiga dengan IllegalArgumentException, dan baris berikutnya berupa stack trace yang menunjuk lokasi kejadiannya. Keluaran di atas hanya menampilkan tiga baris pertama agar ringkas. Pemisahan ini berguna ketika kekosongan memang tidak memiliki kelanjutan yang wajar, misalnya identitas tugas yang wajib ada. Pesan exceptionnya ditulis sendiri oleh pengembang, sehingga penyebab kegagalan terbaca langsung dari keluaran program. Jenis exception yang dipilih juga boleh diganti dengan yang lebih spesifik terhadap keadaannya.
Pilihan antara nilai bawaan dan throw mengikuti keseriusan kekosongan. Nilai bawaan dipilih apabila program masih bermakna tanpa nilai tersebut, misalnya perhitungan sementara yang boleh diisi nol. Throw dipilih apabila kelanjutan programnya keliru tanpa nilai itu, sehingga berhenti lebih awal lebih baik daripada melanjutkan dengan data yang salah.
Ringkasan#
- Safe call
?.memanggil anggota hanya bila nilai terisi dan menghasilkan null sebaliknya, sehingga rantai yang panjang pun tetap aman. - Elvis operator
?:mengambil sisi kiri bila terisi dan nilai bawaan di sisi kanan bila null, sehingga hasilnya bebas dari null. - Sisi kanan elvis operator boleh berupa
throw, yang menghentikan program bila kekosongan memang tidak dapat dilanjutkan. - Pola
?.letmenjalankan blok kode hanya untuk nilai yang terisi.
Referensi#
- Null safety — Kotlin Documentation — Dokumentasi resmi safe call dan elvis operator beserta contohnya.
- Idioms — Kotlin Documentation — Daftar idiom resmi Kotlin yang memuat pola penanganan null yang disarankan.