Seleksi Kondisi #
Setiap program yang berguna perlu membuat keputusan: lakukan ini jika kondisi terpenuhi, lakukan itu jika tidak. Di hampir semua bahasa pemrograman, keputusan ini dibuat dengan if dan switch. Kotlin mengambil konsep yang sama tapi mendorongnya lebih jauh: if dan when bukan sekadar statement — keduanya adalah ekspresi yang mengembalikan nilai. Ini bukan sekadar detail teknis kecil. Kemampuan menggunakan struktur kontrol sebagai ekspresi membuat kode lebih deklaratif, lebih mudah dibaca, dan mengurangi kebutuhan akan variabel sementara. Artikel ini membahas cara menggunakan if dan when secara idiomatis di Kotlin — dari bentuk paling dasar hingga pola-pola yang membuat kode lebih ekspresif.
if sebagai Statement
#
Bentuk paling dasar if bekerja persis seperti di Java atau bahasa lain: eksekusi blok kode jika kondisi terpenuhi.
val suhu = 38.5
if (suhu >= 37.5) {
println("Demam — istirahat dan minum air putih")
}
Tambahkan else untuk menangani kondisi yang tidak terpenuhi:
val saldo = 150_000
val hargaBelanja = 200_000
if (saldo >= hargaBelanja) {
println("Pembayaran berhasil")
} else {
println("Saldo tidak cukup. Kekurangan: ${hargaBelanja - saldo}")
}
Dan else if untuk kondisi bertingkat:
val nilai = 78
if (nilai >= 90) {
println("Grade A — Sangat Baik")
} else if (nilai >= 80) {
println("Grade B — Baik")
} else if (nilai >= 70) {
println("Grade C — Cukup")
} else if (nilai >= 60) {
println("Grade D — Kurang")
} else {
println("Grade E — Tidak Lulus")
}
if sebagai Ekspresi
#
Di Kotlin, if mengembalikan nilai dari blok yang dieksekusi. Nilai terakhir dalam sebuah blok adalah nilai yang dikembalikan. Ini menghilangkan kebutuhan operator ternary (kondisi ? a : b) yang ada di Java, JavaScript, dan bahasa lain.
val angka = 42
// if sebagai ekspresi — hasil disimpan ke variabel
val keterangan = if (angka > 0) "positif" else "nol atau negatif"
println(keterangan) // positif
// Versi multiline — nilai terakhir tiap blok yang dikembalikan
val kategori = if (angka > 100) {
println("Menghitung kategori besar...")
"besar"
} else if (angka > 10) {
println("Menghitung kategori sedang...")
"sedang"
} else {
"kecil"
}
println(kategori) // sedang
if Ekspresi Menggantikan Ternary
#
Karena Kotlin tidak punya operator ternary, if ekspresi adalah penggantinya:
val umur = 20
val status = "Kamu adalah " + if (umur >= 18) "dewasa" else "anak-anak"
// Dalam string template
println("Status: ${if (umur >= 18) "dewasa" else "anak-anak"}")
// Sebagai argumen fungsi
tampilkan(if (aktif) "Aktif" else "Nonaktif")
Syarat if sebagai Ekspresi
#
Ketika if digunakan sebagai ekspresi (nilainya dipakai), else wajib ada. Tanpa else, compiler tidak tahu nilai apa yang dikembalikan jika kondisi tidak terpenuhi.
// ANTI-PATTERN: if ekspresi tanpa else — tidak bisa dikompilasi
val hasil = if (x > 0) "positif" // ✗ error: 'if' must have both main and else branches
// BENAR: selalu ada else jika digunakan sebagai ekspresi
val hasil = if (x > 0) "positif" else "tidak positif"
Guard Clause — Early Return dengan if
#
Salah satu pola terpenting dalam pengkodean bersih adalah guard clause: periksa kondisi tidak valid di awal fungsi dan kembalikan lebih awal, daripada membungkus seluruh logika dalam kondisi if bersarang.
// ANTI-PATTERN: logika dibungkus dalam if bersarang (pyramid of doom)
fun prosesTransaksi(pengguna: Pengguna?, jumlah: Double) {
if (pengguna != null) {
if (pengguna.aktif) {
if (jumlah > 0) {
if (pengguna.saldo >= jumlah) {
// baru di sini logika utama...
pengguna.saldo -= jumlah
println("Transaksi berhasil")
} else {
println("Saldo tidak cukup")
}
} else {
println("Jumlah harus positif")
}
} else {
println("Akun tidak aktif")
}
} else {
println("Pengguna tidak ditemukan")
}
}
// BENAR: guard clause — setiap kondisi invalid ditangani lebih awal
fun prosesTransaksi(pengguna: Pengguna?, jumlah: Double) {
if (pengguna == null) {
println("Pengguna tidak ditemukan")
return
}
if (!pengguna.aktif) {
println("Akun tidak aktif")
return
}
if (jumlah <= 0) {
println("Jumlah harus positif")
return
}
if (pengguna.saldo < jumlah) {
println("Saldo tidak cukup")
return
}
// Logika utama — hanya dicapai jika semua kondisi terpenuhi
pengguna.saldo -= jumlah
println("Transaksi berhasil")
}
Guard clause membuat logika utama tetap rata di kiri, tidak tertimbun dalam indentasi yang dalam. Setiap return awal adalah penjelasan eksplisit mengapa fungsi tidak bisa melanjutkan.
when — Pengganti Switch yang Jauh Lebih Kuat
#
when di Kotlin jauh melampaui switch di Java. Ia bisa mencocokkan nilai tunggal, range, tipe, kondisi arbitrer, atau kombinasinya — dan seperti if, ia juga adalah ekspresi.
when Dasar
#
val hariKe = 3
val namaHari = when (hariKe) {
1 -> "Senin"
2 -> "Selasa"
3 -> "Rabu"
4 -> "Kamis"
5 -> "Jumat"
6 -> "Sabtu"
7 -> "Minggu"
else -> "Hari tidak valid"
}
println(namaHari) // Rabu
Multiple Value dalam Satu Branch #
Beberapa nilai bisa ditangani dalam satu branch dengan memisahkan menggunakan koma:
val bulanKe = 4
val musim = when (bulanKe) {
12, 1, 2 -> "Musim Hujan (Puncak)"
3, 4, 5 -> "Musim Peralihan ke Kemarau"
6, 7, 8 -> "Musim Kemarau"
9, 10, 11 -> "Musim Peralihan ke Hujan"
else -> "Bulan tidak valid"
}
println(musim) // Musim Peralihan ke Kemarau
when dengan Range
#
val skor = 85
val grade = when (skor) {
in 90..100 -> "A"
in 80..89 -> "B"
in 70..79 -> "C"
in 60..69 -> "D"
in 0..59 -> "E"
else -> "Skor tidak valid"
}
println(grade) // B
when sebagai Statement vs Ekspresi
#
Seperti if, when bisa digunakan sebagai statement atau ekspresi:
val kode = 404
// Sebagai statement (tidak perlu else)
when (kode) {
200 -> println("OK")
404 -> println("Tidak ditemukan")
500 -> println("Error server")
}
// Sebagai ekspresi (else wajib jika tidak exhaustive)
val pesan = when (kode) {
200 -> "Berhasil"
201 -> "Dibuat"
400 -> "Permintaan tidak valid"
401 -> "Tidak terotorisasi"
403 -> "Dilarang"
404 -> "Tidak ditemukan"
in 500..599 -> "Error server"
else -> "Status tidak dikenal: $kode"
}
Exhaustive when — Keamanan Ekstra dari Compiler
#
Ketika when digunakan sebagai ekspresi dengan enum atau sealed class, Kotlin bisa memeriksa apakah semua kemungkinan sudah ditangani. Ini disebut exhaustive check — dan ini adalah fitur yang sangat berharga untuk mencegah bug.
enum class StatusPesanan {
MENUNGGU, DIPROSES, DIKIRIM, SELESAI, DIBATALKAN
}
fun labelStatus(status: StatusPesanan): String {
return when (status) {
StatusPesanan.MENUNGGU -> "Menunggu konfirmasi"
StatusPesanan.DIPROSES -> "Sedang diproses"
StatusPesanan.DIKIRIM -> "Dalam pengiriman"
StatusPesanan.SELESAI -> "Pesanan selesai"
StatusPesanan.DIBATALKAN -> "Pesanan dibatalkan"
// Tidak perlu else — compiler tahu semua kasus sudah ditangani
}
}
Manfaat exhaustive check terasa saat enum diperluas. Jika kamu menambahkan entry baru ke StatusPesanan, compiler langsung memberikan error di semua when yang belum menangani entry baru tersebut — tidak ada kasus yang terlewat secara diam-diam.
// Setelah menambah StatusPesanan.DIKEMBALIKAN ke enum:
// fun labelStatus() akan error kompilasi — memaksamu menangani kasus baru
Exhaustive when dengan Sealed Class
#
Sealed class memberi manfaat exhaustive check yang sama, tapi untuk hierarki tipe yang lebih kompleks:
sealed class HasilOperasi {
data class Berhasil(val data: String) : HasilOperasi()
data class Gagal(val pesan: String, val kode: Int) : HasilOperasi()
object Memuat : HasilOperasi()
}
fun tanganiHasil(hasil: HasilOperasi) {
when (hasil) {
is HasilOperasi.Berhasil -> println("Data: ${hasil.data}")
is HasilOperasi.Gagal -> println("Error ${hasil.kode}: ${hasil.pesan}")
is HasilOperasi.Memuat -> println("Sedang memuat...")
// Tidak perlu else — semua subtype sudah dicakup
}
}
tanganiHasil(HasilOperasi.Berhasil("respons dari server"))
tanganiHasil(HasilOperasi.Gagal("Koneksi terputus", 503))
tanganiHasil(HasilOperasi.Memuat)
when dengan Type Check
#
when bisa digunakan untuk mencocokkan tipe dan secara otomatis melakukan smart cast di dalam branch yang cocok:
fun prosesNilai(nilai: Any): String {
return when (nilai) {
is String -> "Teks '${nilai.uppercase()}' (${nilai.length} karakter)"
is Int -> "Bilangan bulat: ${nilai * 2}"
is Double -> "Desimal: ${"%.2f".format(nilai)}"
is Boolean -> if (nilai) "Benar" else "Salah"
is List<*> -> "Daftar berisi ${nilai.size} elemen"
is Map<*, *> -> "Map berisi ${nilai.size} pasang data"
null -> "Nilai kosong (null)"
else -> "Tipe tidak dikenal: ${nilai::class.simpleName}"
}
}
println(prosesNilai("kotlin")) // Teks 'KOTLIN' (6 karakter)
println(prosesNilai(42)) // Bilangan bulat: 84
println(prosesNilai(3.14)) // Desimal: 3.14
println(prosesNilai(listOf(1, 2, 3))) // Daftar berisi 3 elemen
println(prosesNilai(null)) // Nilai kosong (null)
Di dalam setiap branch is, Kotlin otomatis melakukan smart cast — tidak perlu cast eksplisit. Di branch is String, nilai sudah langsung bisa diperlakukan sebagai String.
when tanpa Argumen
#
when tanpa argumen berfungsi sebagai pengganti if-else chain yang panjang. Setiap branch bisa berupa ekspresi Boolean apapun.
val suhu = 35.0
val kelembapan = 80
val kondisiCuaca = when {
suhu >= 38 -> "Sangat panas dan berbahaya"
suhu >= 35 && kelembapan >= 80 -> "Panas dan lembap — terasa lebih gerah"
suhu >= 35 -> "Panas"
suhu >= 25 -> "Hangat"
suhu >= 15 -> "Sejuk"
suhu >= 0 -> "Dingin"
else -> "Sangat dingin — di bawah titik beku"
}
println(kondisiCuaca) // Panas dan lembap — terasa lebih gerah
Ini jauh lebih bersih dari serangkaian if-else if ketika kondisi kompleks dan tidak semua bergantung pada satu variabel yang sama.
when dengan Blok Multi-Statement
#
Setiap branch when bisa berisi blok kode multiline. Nilai terakhir dalam blok adalah nilai yang dikembalikan (jika when digunakan sebagai ekspresi):
val aksi = "BAYAR"
val jumlah = 150_000.0
val hasil = when (aksi) {
"BAYAR" -> {
println("Memproses pembayaran sebesar Rp${jumlah.toLong()}...")
val biayaAdmin = jumlah * 0.01
val total = jumlah + biayaAdmin
"Pembayaran berhasil. Total: Rp${total.toLong()}" // nilai yang dikembalikan
}
"REFUND" -> {
println("Memproses pengembalian dana...")
"Refund akan diproses dalam 3-5 hari kerja"
}
"CEK_SALDO" -> {
println("Mengambil data saldo...")
"Saldo saat ini: Rp500.000"
}
else -> "Aksi tidak dikenal: $aksi"
}
println(hasil)
when dengan Kondisi Tambahan — Guard
#
Branch when bisa ditambahkan kondisi tambahan menggunakan if setelah pola:
data class Pengguna(val nama: String, val umur: Int, val premium: Boolean)
fun kategorikanPengguna(pengguna: Pengguna): String {
return when {
pengguna.premium && pengguna.umur >= 18 -> "Premium Dewasa"
pengguna.premium && pengguna.umur < 18 -> "Premium Junior"
!pengguna.premium && pengguna.umur >= 18 -> "Reguler Dewasa"
else -> "Reguler Junior"
}
}
Alur Kontrol dalam Kotlin #
Memahami hubungan antara if dan when membantu memilih yang tepat untuk setiap situasi:
flowchart TD
A{Kondisi yang\nperlu diperiksa?} --> B{Satu variabel\ndengan banyak nilai?}
B -- Ya --> C["when(variabel) { ... }"]
B -- Tidak --> D{Kondisi kompleks\nberbeda-beda?}
D -- Ya --> E["when { kondisi -> ... }"]
D -- Tidak --> F{Perlu nilai\nhasil kondisi?}
F -- Ya --> G["val x = if (kondisi) a else b"]
F -- Tidak --> H["if (kondisi) { ... } else { ... }"]
C --> I{Semua kasus\nharus ditangani?}
I -- Ya --> J["Gunakan enum/sealed class\nuntuk exhaustive check"]
I -- Tidak --> K["Tambahkan else"]Memilih antara if dan when
#
GUNAKAN if jika:
✓ Satu atau dua kondisi sederhana
✓ Kondisi melibatkan perbandingan range atau ekspresi Boolean bebas
✓ Guard clause (early return) di awal fungsi
✓ Pengganti ternary operator: val x = if (a) b else c
GUNAKAN when jika:
✓ Mencocokkan satu variabel dengan banyak nilai yang mungkin
✓ Menangani berbagai tipe dengan smart cast
✓ Menggantikan if-else if chain yang panjang (lebih dari 3 kondisi)
✓ Bekerja dengan enum atau sealed class untuk exhaustive check
✓ Kode lebih mudah dibaca dengan when daripada if-else bertingkat
Contoh Nyata: Memproses Respons HTTP #
data class ResponsHttp(val kode: Int, val isi: String?)
fun tanganiRespons(respons: ResponsHttp): String {
// Guard clause: validasi dasar dulu
if (respons.kode < 100 || respons.kode > 599) {
return "Kode respons tidak valid: ${respons.kode}"
}
// when untuk penanganan berdasarkan kode
return when (respons.kode) {
200 -> "Berhasil: ${respons.isi ?: "Tidak ada isi"}"
201 -> "Data baru dibuat"
204 -> "Berhasil tanpa isi"
301, 302 -> "Dialihkan ke URL lain"
400 -> "Permintaan tidak valid — periksa parameter"
401 -> "Autentikasi diperlukan — silakan login"
403 -> "Akses ditolak"
404 -> "Resource tidak ditemukan"
429 -> "Terlalu banyak permintaan — coba lagi nanti"
in 500..599 -> {
val pesanError = respons.isi ?: "Tidak ada detail"
"Error server (${respons.kode}): $pesanError"
}
else -> "Kode respons tidak dikenal: ${respons.kode}"
}
}
println(tanganiRespons(ResponsHttp(200, "Data pengguna")))
println(tanganiRespons(ResponsHttp(404, null)))
println(tanganiRespons(ResponsHttp(503, "Service Unavailable")))
Output:
Berhasil: Data pengguna
Resource tidak ditemukan
Error server (503): Service Unavailable
Ringkasan #
ifadalah ekspresi — di Kotlin,ifmengembalikan nilai sehingga bisa digunakan langsung di sisi kanan assignment, sebagai argumen fungsi, atau dalam string template. Ini menghilangkan kebutuhan operator ternary.elsewajib saatifdigunakan sebagai ekspresi — compiler butuh nilai untuk semua kemungkinan kondisi. Jika digunakan sebagai statement (hasilnya tidak dipakai),elseopsional.- Guard clause lebih baik dari if bersarang — periksa kondisi tidak valid di awal fungsi dengan
returnlebih awal, daripada membungkus logika utama dalam piramidaifyang dalam.whenadalah switch yang jauh lebih kuat — bisa mencocokkan nilai tunggal, multiple value, range, tipe, dan kondisi Boolean bebas. Setiap branch bisa berupa nilai tunggal, blok multiline, atau kombinasinya.whentanpa argumen menggantikan if-else chain — jika lebih dari dua atau tiga kondisi perlu diperiksa dengan kondisi yang berbeda-beda,when { }lebih bersih dariif-else if-else if.- Exhaustive check dengan enum dan sealed class — ketika
whendigunakan sebagai ekspresi dengan enum atau sealed class, compiler memverifikasi semua kasus sudah ditangani. Tambahan entry baru otomatis terdeteksi sebagai error kompilasi.- Smart cast dalam
when— setelah branchis TipeX, variabel otomatis diperlakukan sebagaiTipeXtanpa cast eksplisit.whensebagai ekspresi butuhelse— kecuali jika semua kemungkinan sudah exhaustive (enum/sealed class). Jika digunakan sebagai statement,elseopsional.