第3章 控制流
控制流决定了程序往哪走。Kotlin 的控制流有一个核心设计:几乎所有控制结构都是表达式,会返回一个值。这一点和 Java 差别很大,后面写代码你会感受到它带来的简洁。
3.1 if 是表达式
Java 里 if 是语句,不能赋值给变量。Kotlin 里 if 可以返回值:
kotlin
val score = 85
// 传统写法
val grade1: String
if (score >= 60) {
grade1 = "及格"
} else {
grade1 = "不及格"
}
// 表达式写法:if 整个作为右值
val grade2 = if (score >= 60) "及格" else "不及格"
// 多行写法也可以,最后一个表达式就是返回值
val grade3 = if (score >= 90) {
"优秀"
} else if (score >= 60) {
"良好"
} else {
"不及格"
}
注意:Kotlin 没有三元运算符 condition ? a : b。因为 if-else 本身就能当表达式用,三元运算符就多余了。
3.2 when 表达式
when 相当于 Java 的 switch,但强大得多。它可以替代 if-else if 链,而且同样是表达式。
3.2.1 基本用法
kotlin
val day = 3
when (day) {
1 -> println("周一")
2 -> println("周二")
3, 4, 5 -> println("工作日") // 多个条件用逗号
in 6..7 -> println("周末") // 用区间
else -> println("未知") // 兜底分支,类似 default
}
3.2.2 作为表达式赋值
kotlin
val status = when {
score >= 90 -> "优秀"
score >= 60 -> "及格"
else -> "不及格"
}
注意第二种写法 when 后面不带参数,每个分支直接写布尔条件。这种写法用来替代 if-else if 链非常清爽。
3.2.3 检查类型
when 配合 is 做类型判断,编译器会自动做类型转换(smart cast):
kotlin
fun describe(obj: Any): String = when (obj) {
is Int -> "整数:$obj"
is String -> "字符串,长度 ${obj.length}" // obj 自动当成 String
is IntArray -> "数组大小 ${obj.size}"
else -> "其他类型"
}
3.2.4 when 的穷尽性
当 when 作为表达式赋值时,编译器会检查分支是否穷尽。碰到密封类(第 7 章讲)时尤其有用------你漏写一个子类,编译器直接报错。普通 when 语句(不赋值)则不要求穷尽。这个特性让 when 比 Java 的 switch 安全得多:Java 漏写 break 会穿透,Kotlin 漏分支会编译报错。
3.3 区间与数列
.. 操作符创建区间:
kotlin
val r1 = 1..10 // 闭区间 [1, 10],包含 10
val r2 = 1 until 10 // 半开区间 [1, 10),不包含 10
val r3 = 10 downTo 1 // 倒序
val r4 = 1..10 step 2 // 步长 2:1, 3, 5, 7, 9
val r5 = 'a'..'z' // 字符区间也可以
区间本质是 IntRange、CharRange 这些类,它们实现了 Iterable,所以能直接 for 遍历。
3.4 for 循环
Kotlin 没有 Java 那种三段式 for (int i = 0; i < n; i++)。它统一用 for (item in collection):
kotlin
// 遍历区间
for (i in 1..5) print(i) // 12345
// 遍历数组/列表
val names = listOf("张三", "李四", "王五")
for (name in names) println(name)
// 带下标
for ((index, name) in names.withIndex()) {
println("$index: $name")
}
// 遍历 Map
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
println("$key -> $value")
}
如果真的需要按索引遍历,用 indices:
kotlin
val arr = arrayOf("A", "B", "C")
for (i in arr.indices) println(arr[i])
3.5 while 与 do-while
这两个和 Java 几乎一样:
kotlin
var n = 3
while (n > 0) {
println("n = $n")
n--
}
var m: Int
do {
m = readln().toInt() // 读一行输入
println("你输入了 $m")
} while (m != 0)
3.6 break、continue 与标签
Kotlin 的 break 和 continue 行为和 Java 相同,但嵌套循环时想跳出外层循环,需要用标签:
kotlin
// 标签格式:名字@
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i + j == 4) break@outer // 直接跳出外层循环
println("i=$i, j=$j")
}
}
3.6.1 Lambda 里的 return
Kotlin 有个容易混淆的点:在 Lambda 里写 return,默认返回的是包含这个 Lambda 的函数,而不是 Lambda 本身。看例子:
kotlin
fun listWalk() {
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach {
if (it == 3) return // 从 listWalk() 返回!不是从 forEach 返回
println(it)
}
println("这行不会打印")
}
// 输出:1 2,然后函数结束
如果只想跳过当前这一次迭代,用标签:
kotlin
fun listWalk2() {
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach lit@ {
if (it == 3) return@lit // 只跳这一轮,继续下一个
println(it)
}
println("循环结束")
}
// 输出:1 2 4 5 循环结束
更常用的写法是隐式标签------标签名和函数名同名:
kotlin
numbers.forEach {
if (it == 3) return@forEach // 等价于上面的 lit@
println(it)
}
这个行为后面讲 Lambda 时还会再遇到,先记住:return 在 Lambda 里默认是"非局部返回"。
3.7 本章小结
if是表达式,可以直接赋值;Kotlin 没有三元运算符。when替代switch和if-else if链,支持多值、区间、类型判断。- 区间
..、until、downTo、step都是ClosedRange/IntProgression。 for统一遍历区间、数组、集合、Map;带下标用withIndex()。break/continue配合标签跳出外层循环;Lambda 里return默认从外层函数返回,用return@标签跳过当前迭代。
动手练习
- 写一个函数,输入分数(0--100),用
when返回等级:90+ 优秀,80--89 良好,60--79 及格,其余不及格。 - 用嵌套循环打印九九乘法表,要求当乘积超过 20 时用标签跳出外层循环。
- 写一段代码遍历
1..100,用continue跳过所有奇数,只打印偶数。