Kotlin 控制流

控制流是程序的"神经系统"——决定代码按什么顺序执行。Kotlin 的控制流语法有几个关键改进:if/when/try 都是表达式(有返回值),when 是加强版 switch,for 直接遍历区间和集合(去掉了易错的 C 风格 for)。本篇逐个讲。

1. if 表达式(关键差异)

Kotlin 的 if 不只是语句,更是表达式——有返回值,可以赋值给变量:

// Kotlin 的 if 是【表达式】(有返回值)
val max = if (a > b) a else b

// 多行 if 分支:最后一行是返回值
val result = if (score >= 90) {
    println("优秀")
    "A"
} else if (score >= 60) {
    println("及格")
    "B"
} else {
    println("不及格")
    "C"
}

// 等价于 Java 的三元 ? :,但 Kotlin 没有三元运算符
// 因为 if-else 已经够简洁了
val s = if (x > 0) "正" else "负"

这是 Kotlin 比 Java 简洁的根源之一。Java 里要先声明 String result;,再 if-else 各赋值,啰嗦还容易忘记初始化。Kotlin 一行搞定。

注意:Kotlin 没有三元运算符 ?:,因为 if (cond) a else b 已经够简洁了。

2. when 表达式(加强版 switch)

Kotlin 的 when 是 Java switch 的全面升级版——支持值匹配、区间、类型、任意条件,而且能作表达式:

// when:Kotlin 的加强版 switch
val x = 3
val desc = when (x) {
    0 -> "零"
    1, 2, 3 -> "1-3"           // 多个值用逗号
    in 4..9 -> "个位"           // 区间
    in 10..99 -> "两位数"
    else -> "其他"
}

// when 是表达式,有返回值;也可以作语句
when (dayOfWeek) {
    1, 2, 3, 4, 5 -> println("工作日")
    6, 7 -> println("周末")
    else -> println("无效")
}

// when 还可以匹配类型(配合 is)
fun describe(obj: Any): String = when (obj) {
    is Int -> "整数"
    is String -> "字符串"
    is List<*> -> "列表"
    else -> "未知"
}

// when 甚至可以匹配任意条件(每个分支是 Boolean 表达式)
val n = 15
when {
    n % 15 == 0 -> println("FizzBuzz")
    n % 3 == 0 -> println("Fizz")
    n % 5 == 0 -> println("Buzz")
    else -> println(n)
}

when 的强大之处:

对比 Java 12+ 引入的 switch 表达式,Kotlin 的 when 仍然更强大、更优雅,而且已经稳定多年。

3. for 循环(区间与集合遍历)

Kotlin 去掉了 C 风格的 for (int i=0; i<n; i++),改用区间和集合遍历,更安全(不会越界)也更简洁:

// for 循环:遍历区间、集合、数组
for (i in 1..5) print("$i ")         // 1 2 3 4 5 (闭区间)
for (i in 1 until 5) print("$i ")    // 1 2 3 4 (半开)
for (i in 5 downTo 1) print("$i ")   // 5 4 3 2 1 (递减)
for (i in 1..10 step 2) print("$i ") // 1 3 5 7 9 (步长)

// 遍历集合
val list = listOf("苹果", "香蕉", "橙子")
for (fruit in list) {
    println(fruit)
}

// 带索引遍历
for ((index, value) in list.withIndex()) {
    println("[$index] $value")
}

// 遍历 Map
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
    println("$key -> $value")
}

// Kotlin 没有 Java 的传统 for(int i=0; i<n; i++)
// 用区间替代,更安全(不会有越界)

注意 for 循环的本质是调用对象的 iterator() 方法。任何类只要实现了 operator fun iterator(): Iterator<T> 都能用 for 遍历。

4. while 与 do-while

while 循环行为和 Java/C 一致。条件为 true 就一直跑:

// while 循环:条件为 true 就一直跑
var i = 0
while (i < 5) {
    println(i)
    i++
}

// do-while:至少跑一次
var n = 0
do {
    println(n)
    n++
} while (n < 3)

// 实际项目里 while 远不如 for 常用
// 因为大多数场景都能用 for/forEach/map/filter 替代
// while 适合"不确定循环次数"的场景(读 IO、消费队列)

实际项目里 while 远不如 for 常用——大多数场景用 forEachmapfilter 等高阶函数替代,代码更声明式。while 适合"循环次数不确定"的场景(读 IO、消费消息队列、轮询)。

5. break、continue 与标签

break(跳出循环)、continue(跳过本次)行为正常。Kotlin 额外支持标签(label),可以指定跳出哪一层:

// break:跳出最近的循环
for (i in 1..10) {
    if (i == 5) break
    print("$i ")        // 1 2 3 4
}

// continue:跳过本次,进入下一次
for (i in 1..10) {
    if (i % 2 == 0) continue
    print("$i ")        // 1 3 5 7 9
}

// 标签(label):break/continue 指定跳出哪一层
outer@ for (i in 1..3) {
    for (j in 1..3) {
        if (i * j > 4) break@outer    // 直接跳出外层
        println("i=$i j=$j")
    }
}

// return 在 lambda 里默认返回外层函数
// 用标签可以只返回 lambda
listOf(1, 2, 3).forEach lit@{
    if (it == 2) return@lit           // 只跳过这次 forEach
    println(it)
}
// 还有隐式标签:forEach 函数名作标签
listOf(1, 2, 3).forEach {
    if (it == 2) return@forEach
    println(it)
}

标签语法:在循环前 名字@,在 break/continue 后 @名字。看起来像 goto,但只用于循环控制,不会污染代码结构。

6. return 与 lambda 的陷阱

return 在普通函数里很直白,但在 lambda 里有个陷阱:默认 return 是从外层函数返回,不是从 lambda!这点容易让初学者困惑:

// return:从函数返回
fun sum(a: Int, b: Int): Int {
    return a + b
}

// 表达式函数体:直接用 = 写返回值
fun double(n: Int) = n * 2

// 单表达式 return
fun isAdult(age: Int): Boolean = age >= 18

// Nothing 返回类型:抛异常的函数
fun fail(msg: String): Nothing {
    throw IllegalArgumentException(msg)
}

// 在 lambda 中 return 的"陷阱":
// 默认 return 是从【外层 fun】返回,不是从 lambda 返回!
fun findFirstEven(nums: List<Int>): Int {
    nums.forEach {
        if (it % 2 == 0) return it      // 这个 return 返回的是 findFirstEven
    }
    return -1
}

解决办法:

7. 实战:计算 FizzBuzz

fun fizzBuzz(n: Int): String =
    when {
        n % 15 == 0 -> "FizzBuzz"
        n % 3 == 0 -> "Fizz"
        n % 5 == 0 -> "Buzz"
        else -> n.toString()
    }

fun main() {
    for (i in 1..20) {
        print("${fizzBuzz(i)} ")
    }
    println()
}
// 输出: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz 

注意 when 不带参数的写法——每个分支都是独立的 Boolean 表达式。这比 Java 的 if-else if 链清晰得多。

8. 实战:猜数字游戏

import kotlin.random.Random

fun main() {
    val target = Random.nextInt(1, 101)
    var attempts = 0

    println("猜一个 1-100 的数字")

    while (true) {
        print("你的猜测: ")
        val input = readlnOrNull()?.toIntOrNull()

        if (input == null) {
            println("请输入有效数字")
            continue
        }

        attempts++

        when {
            input < target -> println("小了")
            input > target -> println("大了")
            else -> {
                println("恭喜,${attempts} 次猜对!")
                return
            }
        }
    }
}

用到了 when、while(true)、可空调用、readlnOrNull。这是 Kotlin 命令行小程序的典型套路。

小结

这一章你掌握了 Kotlin 所有控制流:if/when 表达式、for(区间与集合)、while、break/continue/标签、return 与 lambda。最大的特色是if 和 when 都是表达式,很多 Java 啰嗦的赋值分支代码能合并成一行。下一篇看 Kotlin 的函数——把这些控制流组织成可复用单元。

← 上一篇 Kotlin 运算符

下一篇 Kotlin 函数

✈️💬