控制流

Kotlin 的控制流包括 ifwhenforwhiledo-while 等。与 Java 最大的区别是:很多控制结构既可以作为语句执行动作,也可以作为表达式返回值。

if 表达式

作为语句:

if (score >= 60) {
    println("通过")
} else {
    println("未通过")
}

作为表达式:

val result = if (score >= 60) "通过" else "未通过"

这就是 Kotlin 不需要三元运算符的原因。Java 中常写:

String result = score >= 60 ? "通过" : "未通过";

Kotlin 用 if 统一表达:

val result = if (score >= 60) "通过" else "未通过"

如果分支是代码块,最后一个表达式就是这个分支的返回值:

val level = if (score >= 90) {
    println("优秀")
    "A"
} else {
    println("继续努力")
    "B"
}

when

when 可以看作更强大的 switch

fun roleText(role: String): String =
    when (role) {
        "admin" -> "管理员"
        "editor" -> "编辑"
        "viewer" -> "访客"
        else -> "未知角色"
    }

与 Java switch 相比:

  • 不需要 break
  • 可以作为表达式返回值。
  • 分支可以匹配类型、范围、多个值或任意条件。

多值分支

fun isWeekend(day: String): Boolean =
    when (day) {
        "Saturday", "Sunday" -> true
        else -> false
    }

范围匹配

fun grade(score: Int): String =
    when (score) {
        in 90..100 -> "A"
        in 80..89 -> "B"
        in 60..79 -> "C"
        in 0..59 -> "D"
        else -> "非法分数"
    }

类型匹配与智能转换

fun describe(value: Any): String =
    when (value) {
        is String -> "字符串长度 ${value.length}"
        is Int -> "整数 ${value + 1}"
        is List<*> -> "列表大小 ${value.size}"
        else -> "未知类型"
    }

is String 分支里,value 会被智能转换成 String,可以直接访问 length

无主语 when

当条件不是围绕同一个值判断时,可以使用无主语 when

fun ticketPrice(age: Int, student: Boolean): Int =
    when {
        age < 6 -> 0
        student -> 50
        age >= 65 -> 40
        else -> 100
    }

这比一连串 if-else 更容易扫描。

带主语 when 的 guard conditions

Kotlin 支持在带主语的 when 分支上继续追加条件,这类追加条件称为 guard condition:

sealed interface Animal {
    data class Cat(val mouseHunter: Boolean, val hungry: Boolean) : Animal
    data class Dog(val breed: String) : Animal
}

fun feed(animal: Animal): String =
    when (animal) {
        is Animal.Dog -> "喂狗"
        is Animal.Cat if !animal.mouseHunter -> "喂猫"
        is Animal.Cat if animal.hungry -> "猫正在捕鼠,但它饿了"
        else -> "暂不处理"
    }

is Animal.Cat if !animal.mouseHunter 表示两件事都成立时才进入该分支:

  • 主条件匹配:animalAnimal.Cat
  • guard 条件为真:!animal.mouseHunter

主条件不匹配时,guard 条件不会执行。进入 guard 条件时,主条件带来的智能转换已经生效,所以可以直接访问 animal.mouseHunter

Java 对比:Java switch 的 pattern matching 也在演进,但 Kotlin 的 when 同时承担表达式、类型匹配、智能转换和穷尽检查。guard conditions 的价值是把“先按类型分类,再按额外状态过滤”的逻辑放在同一个分支上,而不是在分支内部再写一层 if

限制:

  • guard conditions 只能用于有主语的 when
  • 不能和逗号分隔的多条件分支一起使用。
  • when 作为表达式时仍然必须穷尽;guard condition 过滤掉的情况也要由后续分支或 else 覆盖。

如果 guard 条件变得很长,优先提取成命名函数:

private fun Animal.Cat.shouldFeed(): Boolean =
    !mouseHunter || hungry

fun feed(animal: Animal): String =
    when (animal) {
        is Animal.Cat if animal.shouldFeed() -> "喂猫"
        is Animal.Cat -> "暂不处理"
        is Animal.Dog -> "喂狗"
    }

穷尽性

when 作为表达式使用时,必须覆盖所有可能情况:

enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

fun label(direction: Direction): String =
    when (direction) {
        Direction.NORTH -> "北"
        Direction.SOUTH -> "南"
        Direction.EAST -> "东"
        Direction.WEST -> "西"
    }

枚举和密封类尤其适合配合 when,因为编译器可以检查是否遗漏分支。相比 Java 中容易漏掉 default 或忘记处理新增枚举值,Kotlin 的穷尽检查更利于维护。

for 循环

遍历集合:

val names = listOf("Ada", "Grace", "Linus")

for (name in names) {
    println(name)
}

遍历键值对:

val ages = mapOf("Ada" to 36, "Grace" to 85)

for ((name, age) in ages) {
    println("$name -> $age")
}

带索引:

for ((index, name) in names.withIndex()) {
    println("$index: $name")
}

范围与步进

for (i in 1..5) {
    print(i) // 12345
}

for (i in 1 until 5) {
    print(i) // 1234
}

for (i in 10 downTo 0 step 2) {
    print(i) // 1086420
}
  • 1..5 包含 5。
  • 1 until 5 不包含 5。
  • downTo 倒序。
  • step 指定步长。

whiledo-while

var index = 0

while (index < 3) {
    println(index)
    index++
}

do-while 至少执行一次:

var input: String

do {
    input = readln()
} while (input.isBlank())

跳转表达式

Kotlin 支持:

  • return
  • break
  • continue

在嵌套 Lambda 中,标签返回很常见:

fun printNonEmpty(values: List<String>) {
    values.forEach { value ->
        if (value.isBlank()) return@forEach
        println(value)
    }
}

return@forEach 的意思是跳过当前 Lambda 调用,而不是从外层函数直接返回。

实践建议

  • 简单赋值优先使用 ifwhen 表达式,减少可变变量。
  • 枚举、密封类配合 when 时,尽量不写无意义的 else,让编译器帮你检查新增分支。
  • 带主语 when 需要额外条件时,优先用 guard conditions,而不是在分支内再嵌套 if
  • 需要索引和值时,优先 withIndex(),不要手写计数器。
  • 复杂条件可以用无主语 when 替代多层 if-else

参考

  • 官方控制流:https://kotlinlang.org/docs/control-flow.html