when adalah salah satu konstruk paling ekspresif di Kotlin. Selain pencocokan nilai sederhana, when mendukung pencocokan tipe lewat is, pencocokan rentang lewat in, deklarasi variabel subject, dan destrukturisasi. Kompilator juga mengecek exhaustiveness untuk sealed class dan enum — bila ada variant yang tidak ditangani, kompilasi gagal. Pada chapter ini kita akan membedah fitur when tingkat lanjut yang stabil di Kotlin 2.0.21, lengkap dengan contoh untuk masing-masing pola, dan menutup dengan ringkasan kapan memakai pola mana.

Exhaustive when untuk Sealed Class #

Saat subject when adalah sealed class atau enum, kompilator mengecek bahwa semua variant ditangani. Bila ada yang terlewat, kompilasi gagal:

kotlin
// when_expressions_lanjutan.kt
// Mendemonstrasikan penggunaan `when` tingkat lanjut:
// - destructuring di when-subject
// - binding variabel subject
// - sealed class dengan data class
// - range dan multiple values
// - when sebagai expression (exhaustive)

// 1. Sealed class dengan data class — `when` exhaustive
sealed interface Hasil {
    data class Sukses(val data: String) : Hasil
    data class Galat(val kode: Int, val pesan: String) : Hasil
    data object Loading : Hasil
    data object Selesai : Hasil
}

fun ringkasHasil(h: Hasil): String = when (h) {
    is Hasil.Sukses -> "Sukses: ${h.data}"
    is Hasil.Galat -> "Galat ${h.kode}: ${h.pesan}"
    Hasil.Loading -> "Sedang loading..."
    Hasil.Selesai -> "Selesai"
}

// 2. Multiple values dan range
fun kategoriNilai(n: Int): String = when (n) {
    0 -> "Nol"
    in 1..59 -> "Remedial"
    in 60..74 -> "Cukup"
    in 75..84 -> "Baik"
    in 85..100 -> "Sangat Baik"
    else -> "Di luar jangkauan"
}

// 3. Pair destructuring (via when subject)
fun prosesPair(p: Pair<String, Int>): String {
    val (nama, umur) = p
    return when {
        umur < 17 -> "$nama belum dewasa (umur $umur)"
        umur in 17..60 -> "$nama usia produktif (umur $umur)"
        else -> "$nama senior (umur $umur)"
    }
}

// 4. when dengan binding variabel subject (`when (val x = ...)`)
fun parseDanKlasifikasi(input: String): String {
    return when (val angka = input.toIntOrNull()) {
        null -> "Input bukan bilangan"
        0 -> "Nol"
        in 1..9 -> "Satuan: $angka"
        in 10..99 -> "Puluhan: $angka"
        else -> "Besar: $angka"
    }
}

// 5. when tanpa subject (chain of boolean)
fun musim(bulan: Int): String = when {
    bulan in 1..3 -> "Hujan"
    bulan in 4..6 -> "Pancaroba"
    bulan in 7..9 -> "Kemarau"
    bulan in 10..12 -> "Pancaroba"
    else -> "Bulan tidak valid"
}

fun main() {
    println("=== 1. Sealed class exhaustive when ===")
    val daftar: List<Hasil> = listOf(
        Hasil.Loading,
        Hasil.Sukses("data pengguna"),
        Hasil.Galat(404, "tidak ditemukan"),
        Hasil.Selesai
    )
    daftar.forEach { println("  ${ringkasHasil(it)}") }

    println()
    println("=== 2. Range dan multiple values ===")
    listOf(0, 45, 70, 80, 95, 150).forEach {
        println("  $it -> ${kategoriNilai(it)}")
    }

    println()
    println("=== 3. Pair destructuring ===")
    println(prosesPair("Budi" to 12))
    println(prosesPair("Siti" to 30))
    println(prosesPair("Dewi" to 70))

    println()
    println("=== 4. when dengan binding variabel subject ===")
    listOf("42", "0", "8", "75", "halo").forEach {
        println("  \"$it\" -> ${parseDanKlasifikasi(it)}")
    }

    println()
    println("=== 5. when tanpa subject ===")
    (1..12).forEach { println("  bulan $it -> ${musim(it)}") }

    println()
    println("=== 6. Ringkasan ===")
    val ringkasan = listOf(
        "when(subject) - cocok nilai/instance",
        "in range - cek angka dalam rentang",
        "is Type - smart cast ke tipe tersebut",
        "binding val x = ... - simpan subject di variabel",
        "when {} tanpa subject - rangkaian boolean"
    )
    ringkasan.forEach { println("  - $it") }
}

Output yang Dihasilkan:

text
=== 1. Sealed class exhaustive when ===
  Sedang loading...
  Sukses: data pengguna
  Galat 404: tidak ditemukan
  Selesai

=== 2. Range dan multiple values ===
  0 -> Nol
  45 -> Remedial
  70 -> Cukup
  80 -> Baik
  95 -> Sangat Baik
  150 -> Di luar jangkauan

=== 3. Pair destructuring ===
Budi belum dewasa (umur 12)
Siti usia produktif (umur 30)
Dewi senior (umur 70)

=== 4. when dengan binding variabel subject ===
  "42" -> Puluhan: 42
  "0" -> Nol
  "8" -> Satuan: 8
  "75" -> Puluhan: 75
  "halo" -> Input bukan bilangan

=== 5. when tanpa subject ===
  bulan 1 -> Hujan
  bulan 2 -> Hujan
  bulan 3 -> Hujan
  bulan 4 -> Pancaroba
  bulan 5 -> Pancaroba
  bulan 6 -> Pancaroba
  bulan 7 -> Kemarau
  bulan 8 -> Kemarau
  bulan 9 -> Kemarau
  bulan 10 -> Pancaroba
  bulan 11 -> Pancaroba
  bulan 12 -> Pancaroba

=== 6. Ringkasan ===
  - when(subject) - cocok nilai/instance
  - in range - cek angka dalam rentang
  - is Type - smart cast ke tipe tersebut
  - binding val x = ... - simpan subject di variabel
  - when {} tanpa subject - rangkaian boolean

Pada cuplikan di atas, when (h) menangani semua empat variant dari sealed interface Hasil. Smart cast bekerja otomatis — setelah is Hasil.Sukses, kita bisa langsung memanggil h.data tanpa cast eksplisit. Bila kita menambahkan variant baru (mis. data object Dibatalkan), kompilator akan menolak when yang lama hingga kita menambahkan branch baru.

Range dan Multiple Values #

when mendukung pencocokan rentang dengan in:

kotlin Pencocokan rentang
fun kategoriNilai(n: Int): String = when (n) {
    0 -> "Nol"
    in 1..59 -> "Remedial"
    in 60..74 -> "Cukup"
    in 75..84 -> "Baik"
    in 85..100 -> "Sangat Baik"
    else -> "Di luar jangkauan"
}

Untuk pencocokan beberapa nilai diskrit (mis. 1, 3, 5), kita bisa menulis 1, 3, 5 -> di satu branch. Rentang cocok untuk skenario seperti kategori nilai atau kode HTTP, sementara multiple values cocok untuk enum subset.

Binding Variabel Subject #

Kita bisa mendeklarasikan variabel subject di dalam when agar bisa dipakai di seluruh branch:

kotlin Binding subject ke variabel
fun parseDanKlasifikasi(input: String): String {
    return when (val angka = input.toIntOrNull()) {
        null -> "Input bukan bilangan"
        0 -> "Nol"
        in 1..9 -> "Satuan: $angka"
        in 10..99 -> "Puluhan: $angka"
        else -> "Besar: $angka"
    }
}

Pola ini sangat berguna saat hasil ekspresi butuh dipakai beberapa kali di cabang yang berbeda — variabel angka tetap berlaku di seluruh branch.

when Tanpa Subject #

Untuk rangkaian kondisi boolean yang berbeda-beda, kita bisa memakai when tanpa subject:

kotlin when tanpa subject (chain of boolean)
fun musim(bulan: Int): String = when {
    bulan in 1..3 -> "Hujan"
    bulan in 4..6 -> "Pancaroba"
    bulan in 7..9 -> "Kemarau"
    bulan in 10..12 -> "Pancaroba"
    else -> "Bulan tidak valid"
}

Pola ini mirip if-else-if tetapi lebih rapi. Branch pertama yang mengembalikan true akan dipilih.

Destructuring Pair dan Data Class #

Kombinasi when dengan destrukturisasi Pair atau data class membuat kode yang memproses tuple menjadi lebih ringkas:

kotlin Destructuring Pair dengan when
fun prosesPair(p: Pair<String, Int>): String {
    val (nama, umur) = p
    return when {
        umur < 17 -> "$nama belum dewasa (umur $umur)"
        umur in 17..60 -> "$nama usia produktif (umur $umur)"
        else -> "$nama senior (umur $umur)"
    }
}

Kita bisa menulis val (nama, umur) = p di luar when dan memakai nilai tersebut di seluruh branch. Untuk lambda, Kotlin mendukung destrukturisasi otomatis lewat sintaks { (a, b) -> ... }.

Ringkasan Pola #

Tabel berikut merangkum pola when yang paling sering dipakai:

PolaSintaksKapan dipakai
Pencocokan nilaiwhen (x) { 1 -> ...; 2 -> ... }Switch sederhana
Pencocokan tipewhen (x) { is String -> ... }Type dispatch, polymorphism
Pencocokan rentangwhen (n) { in 1..10 -> ... }Kategori berbasis rentang
Multiple valueswhen (n) { 1, 3, 5 -> ... }Subset enum / konstanta diskrit
Binding subjectwhen (val x = ...) { ... }Hasil ekspresi butuh dipakai ulang
Tanpa subjectwhen { cond1 -> ...; cond2 -> ... }Rangkaian boolean berbeda-beda

Referensi Lanjutan #

Selanjutnya #

Setelah menguasai when lanjutan, kita lanjut ke fitur tipe lanjutan — definitely non-nullable types (T & Any) — yang memaksa tipe parameter generik non-nullable di scope tertentu.