Safe call ?. menyelesaikan masalah pemanggilan method pada nullable, tetapi hasilnya tetap bertipe nullable. Bila kita ingin nilai konkret alih-alih null, operator Elvis ?: menjadi alat utama. Operator ini mengevaluasi sisi kiri; bila bukan null, hasilnya adalah sisi kiri; bila null, hasilnya adalah sisi kanan. Nama “Elvis” berasal dari kemiripan visual ?: dengan emotikon wajah penyanyi terkenal. Pada chapter ini kita akan membedah penggunaan dasar, ekspresi kompleks di sisi kanan, serta pemanfaatan return dan throw untuk kontrol alur.
Bentuk Dasar dan Sifat Evaluasi #
Bentuk umum operator Elvis adalah kiri ?: kanan. Ekspresi kiri harus bertipe nullable. Sisi kanan dievaluasi hanya bila kiri bernilai null. Bila kiri tidak null, sisi kanan tidak akan pernah dijalankan. Hasil akhir selalu bertipe non-nullable (selama kanan juga non-nullable).
fun main() {
val nama: String? = null
val namaTampilan: String = nama ?: "Anonim"
println("Nama tampilan: $namaTampilan")
val namaAsli: String? = "Budi"
val tampilanAsli: String = namaAsli ?: "Anonim"
println("Nama tampilan asli: $tampilanAsli")
val umur: Int? = null
val umurDefault: Int = umur ?: 0
println("Umur default: $umurDefault")
val daftar: List<String>? = null
val daftarAman: List<String> = daftar ?: emptyList()
println("Ukuran daftar aman: ${daftarAman.size}")
val pengguna: String? = cariPengguna(99)
println("Pengguna ditemukan: ${pengguna ?: "Tidak terdaftar"}")
val id: Long? = null
val idFinal: Long = id ?: run {
println("Menghasilkan ID default karena id null")
0L
}
println("ID final: $idFinal")
val namaLengkap: String? = "Siti"
val salam: String = namaLengkap?.let { "Halo, $it!" } ?: "Halo, tamu!"
println(salam)
prosesNama(null)
prosesNama("Dewi")
val panjang: Int? = 0
val jumlah: Int = panjang ?: 0
println("Jumlah: $jumlah (elvis tidak dipicu karena 0 bukan null)")
val bahasa: String? = null
val daftarBahasa: List<String> = listOf(bahasa ?: "id").also { println("Daftar bahasa: $it") }
println("Akhir program")
}
fun cariPengguna(id: Int): String? {
return if (id == 1) "Budi" else null
}
fun prosesNama(nama: String?) {
val valid: String = nama ?: return
println("Memproses nama: $valid")
}Output yang Dihasilkan:
Nama tampilan: Anonim
Nama tampilan asli: Budi
Umur default: 0
Ukuran daftar aman: 0
Pengguna ditemukan: Tidak terdaftar
Menghasilkan ID default karena id null
ID final: 0
Halo, Siti!
Memproses nama: Dewi
Jumlah: 0 (elvis tidak dipicu karena 0 bukan null)
Daftar bahasa: [id]
Akhir programMari kita uraikan baris-baris penting pada contoh di atas:
- Baris 3 (
nama ?: "Anonim"): bentuk paling sederhana. Sisi kirinull, sehingga hasilnya"Anonim". VariabelnamaTampilanmenjadi bertipeStringnon-nullable. - Baris 6 (
namaAsli ?: "Anonim"): sisi kiri tidaknull, sehingga sisi kanan tidak dievaluasi. Hasilnya"Budi". - Baris 12 (
daftar ?: emptyList()): operator Elvis juga bekerja pada tipe generic.emptyList()dipanggil sebagai nilai default yang aman. - Baris 15 (
cariPengguna(99)): function sendiri bisa mengembalikannull. Operator Elvis memberi teks pengganti tanpa perlu if eksplisit. - Baris 18-21 (
id ?: run { ... }): sisi kanan dapat berupa blok kode kompleks.run { ... }mengeksekusi lambda dan mengembalikan nilai terakhirnya0L. Side effectprintlnhanya terjadi bilaidnull. - Baris 24 (
namaLengkap?.let { ... } ?: "Halo, tamu!"): pola umum menggabungkan?.letdengan Elvis. BilanamaLengkapnull,?.letmenghasilkan null, dan Elvis mengembalikan string default. - Baris 29 (
prosesNama(null)): functionprosesNamamemakai?: returndi sisi kanan. Bilanamanull, function keluar lebih awal tanpa menjalankan baris berikutnya. Ini menunjukkan bahwa sisi kanan Elvis dapat berupa statement kontrol alur. - Baris 33 (
panjang ?: 0): nilai0pada tipe nullable tetap tidak dianggap null oleh operator Elvis. Elvis hanya merespons nilainull, bukan nilai falsy.
Sisi Kanan Berupa return dan throw #
Salah satu fitur paling berguna dari operator Elvis adalah kemampuan menempatkan return, throw, atau ekspresi kompleks di sisi kanan. Hal ini memungkinkan pola early return atau fail-fast yang ringkas.
| Bentuk | Efek | Cocok untuk |
|---|---|---|
x ?: default | Mengganti null dengan default | Memberi nilai pengganti |
x ?: return | Keluar dari function saat x null | Validasi input opsional |
x ?: throw IllegalStateException(...) | Melempar exception saat x null | Pre-condition yang harus terpenuhi |
x ?: error("pesan") | Sama seperti throw IllegalStateException | Sintaks singkat untuk pre-condition |
Aspek Evaluasi dan Short-Circuit #
Operator Elvis hanya mengevaluasi sisi kanan bila sisi kiri adalah null. Hal ini disebut perilaku short-circuit — sama seperti || dan && pada operasi boolean. Konsekuensi praktisnya, kita dapat menempatkan ekspresi yang mahal di sisi kanan tanpa khawatir dievaluasi saat tidak diperlukan. Misalnya konfigurasi ?: muatDariDatabase() tidak akan memanggil database bila konfigurasi sudah tersedia secara lokal.
Perilaku ini juga penting untuk efek samping. Bila sisi kanan berupa blok yang melakukan logging, mengirim telemetri, atau memicu side effect lain, side effect tersebut hanya terjadi bila sisi kiri ternyata null. Karena itu urutan operand sangat penting: nilai yang sudah pasti murah dan cepat dievaluasi sebaiknya ditempatkan di kiri, sementara operasi mahal atau berdampak samping ditaruh di kanan.
Perbandingan dengan if-else Eksplisit #
Operator Elvis selalu dapat ditulis ulang sebagai if (x != null) x else y namun sintaks tersebut jauh lebih panjang. Elvis juga merupakan ekspresi nilai sehingga dapat dipakai langsung dalam argument function, assignment, atau posisi ekspresi lain tanpa perlu blok tambahan. Trade-off-nya, Elvis tidak sefleksibel if bila cabang memerlukan banyak statement — di situasi tersebut, blok if biasanya lebih mudah dibaca.
Referensi Lanjutan #
- Null-safety
— bagian Elvis operator
?:pada panduan null-safety resmi
Dengan operator Elvis, kita memiliki cara ringkas untuk mengubah nilai nullable menjadi non-nullable. Kadang-kadang, kita justru yakin sebuah nilai tidak null meski tipenya nullable. Untuk situasi tersebut, Kotlin menyediakan operator not-null assertion !! yang akan kita bahas pada chapter berikutnya.