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).

kotlin
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:

text
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 program

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

  1. Baris 3 (nama ?: "Anonim"): bentuk paling sederhana. Sisi kiri null, sehingga hasilnya "Anonim". Variabel namaTampilan menjadi bertipe String non-nullable.
  2. Baris 6 (namaAsli ?: "Anonim"): sisi kiri tidak null, sehingga sisi kanan tidak dievaluasi. Hasilnya "Budi".
  3. Baris 12 (daftar ?: emptyList()): operator Elvis juga bekerja pada tipe generic. emptyList() dipanggil sebagai nilai default yang aman.
  4. Baris 15 (cariPengguna(99)): function sendiri bisa mengembalikan null. Operator Elvis memberi teks pengganti tanpa perlu if eksplisit.
  5. Baris 18-21 (id ?: run { ... }): sisi kanan dapat berupa blok kode kompleks. run { ... } mengeksekusi lambda dan mengembalikan nilai terakhirnya 0L. Side effect println hanya terjadi bila id null.
  6. Baris 24 (namaLengkap?.let { ... } ?: "Halo, tamu!"): pola umum menggabungkan ?.let dengan Elvis. Bila namaLengkap null, ?.let menghasilkan null, dan Elvis mengembalikan string default.
  7. Baris 29 (prosesNama(null)): function prosesNama memakai ?: return di sisi kanan. Bila nama null, function keluar lebih awal tanpa menjalankan baris berikutnya. Ini menunjukkan bahwa sisi kanan Elvis dapat berupa statement kontrol alur.
  8. Baris 33 (panjang ?: 0): nilai 0 pada tipe nullable tetap tidak dianggap null oleh operator Elvis. Elvis hanya merespons nilai null, 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.

BentukEfekCocok untuk
x ?: defaultMengganti null dengan defaultMemberi nilai pengganti
x ?: returnKeluar dari function saat x nullValidasi input opsional
x ?: throw IllegalStateException(...)Melempar exception saat x nullPre-condition yang harus terpenuhi
x ?: error("pesan")Sama seperti throw IllegalStateExceptionSintaks 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.