4. 分支和循环:控制程序的“交通灯”
写代码就像指挥交通。什么时候直行,什么时候转弯,什么时候绕圈,都得靠分支和循环来控制。今天我们就来聊聊 Kotlin 里的这些“交通规则”。
本章核心知识点一览:
- if 判断语句与运算符
- if-else 分支结构
- when 表达式(Kotlin 的 switch 升级版)
- while 循环与 do-while 循环
- for 循环与区间
- break 与 continue 控制循环
4.1 if 判断语句:最简单的选择
if 是编程里最基础的判断。说白了就是:如果条件成立,就做某件事。
val age = 18
if (age >= 18) {
println("你已经成年了")
}
这里有个小细节:如果 if 后面只有一行代码,花括号可以省略。但我个人建议永远加上花括号——哪怕只有一行。为什么?因为我在项目中见过太多次因为省略花括号导致的 bug,后来加需求时忘了补括号,结果逻辑全乱套了。
4.2 运算符:判断的“工具包”
写判断离不开运算符。Kotlin 的运算符和其他语言大同小异,但有几个地方值得注意。
| 类型 | 运算符 | 示例 | 说明 |
|---|---|---|---|
| 比较 | ==, != | a == b | 比较值是否相等(不是引用) |
| 比较 | >, <, >=, <= | a > b | 大小比较 |
| 逻辑 | &&, ||, ! | a > 0 && b > 0 | 与、或、非 |
| 区间 | .. | 1..10 | 包含两端,1 到 10 |
| 包含 | in, !in | x in 1..10 | 判断是否在区间内 |
💡 避坑指南:Kotlin 的 == 相当于 Java 的 equals(),比较的是内容。而 === 才是比较引用地址。我曾经帮同事排查过一个 bug,他用 == 比较两个字符串对象,结果一直 false——其实是两个不同对象但内容相同。换成 == 就对了。
4.3 if-else 语句:二选一
现实世界很少只有一种情况。if-else 就是用来处理“要么 A,要么 B”的场景。
val score = 85
if (score >= 60) {
println("及格了")
} else {
println("不及格")
}
Kotlin 有个很酷的特性:if-else 可以作为表达式返回值。这意味着你可以直接赋值。
val result = if (score >= 60) "及格" else "不及格"
println(result) // 输出:及格
嗯,这里要注意:作为表达式时,if 和 else 的最后一个表达式就是返回值。如果只有 if 没有 else,就不能当表达式用——编译器会报错。
4.4 when 表达式:多路分支的优雅写法
如果你有多个分支要判断,用 if-else 链会写得很长。这时候 when 就派上用场了。它比 Java 的 switch 强大得多。
val day = 3
val dayName = when (day) {
1 -> "星期一"
2 -> "星期二"
3 -> "星期三"
4 -> "星期四"
5 -> "星期五"
6, 7 -> "周末"
else -> "非法输入"
}
println(dayName) // 输出:星期三
when 还能做区间判断和类型判断,非常灵活。
val x = 15
when (x) {
in 1..10 -> println("x 在 1 到 10 之间")
in 11..20 -> println("x 在 11 到 20 之间")
else -> println("x 超出范围")
}
val obj: Any = "Hello"
when (obj) {
is String -> println("这是一个字符串,长度:${obj.length}")
is Int -> println("这是一个整数")
else -> println("未知类型")
}
⚠️ 注意:when 的分支条件按顺序匹配,匹配到第一个就停止。所以要把更具体的条件放在前面。我曾经在项目中把 else 放在了中间,结果后面的分支永远执行不到——这种 bug 很难查,因为编译器不会报错。
4.5 while 循环:先判断,再执行
while 循环的逻辑很简单:只要条件为真,就一直执行。适合用在循环次数不确定的场景。
var count = 0
while (count < 5) {
println("第 ${count + 1} 次循环")
count++
}
do-while 和 while 的区别在于:do-while 至少执行一次,因为条件判断在循环体后面。
var n = 10
do {
println("至少执行一次,n = $n")
n++
} while (n < 5) // 条件不满足,但已经执行了一次
我个人很少用 do-while,但有一种场景很合适:读取用户输入直到合法为止。先读一次,如果不对再继续读。
4.6 for 循环:遍历的利器
Kotlin 的 for 循环主要用来遍历区间、数组或集合。写法非常简洁。
// 遍历 1 到 5
for (i in 1..5) {
println(i) // 输出 1 2 3 4 5
}
// 遍历 1 到 4(不包含 5)
for (i in 1 until 5) {
println(i) // 输出 1 2 3 4
}
// 倒序遍历
for (i in 5 downTo 1) {
println(i) // 输出 5 4 3 2 1
}
// 步长为 2
for (i in 1..10 step 2) {
println(i) // 输出 1 3 5 7 9
}
遍历集合也很方便:
val fruits = listOf("苹果", "香蕉", "橘子")
for (fruit in fruits) {
println(fruit)
}
// 带索引遍历
for ((index, fruit) in fruits.withIndex()) {
println("第 $index 个水果是 $fruit")
}
💡 我的习惯:遍历集合时尽量用 for 而不是 while。for 循环更安全,不容易出现索引越界的问题。我在项目中见过有人用 while 遍历列表,结果索引写错了,直接抛 IndexOutOfBoundsException。
4.7 break 和 continue:循环的“刹车”与“跳过”
有时候我们不想让循环一直跑到底。break 用来提前结束整个循环,continue 用来跳过当前这次,继续下一次。
// break 示例:找到第一个偶数就停止
for (i in 1..10) {
if (i % 2 == 0) {
println("找到偶数:$i")
break // 跳出循环
}
}
// continue 示例:只打印奇数
for (i in 1..10) {
if (i % 2 == 0) {
continue // 跳过偶数
}
println(i) // 输出 1 3 5 7 9
}
嵌套循环时,break 和 continue 默认只作用于最内层循环。如果想跳出外层循环,需要配合标签使用。
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) {
break@outer // 跳出外层循环
}
println("i=$i, j=$j")
}
}
⚠️ 避坑指南:我曾经在项目里看到有人滥用 break 和 continue,导致循环逻辑变得很难理解。我的建议是:能用 for 循环的区间控制解决的问题,就不要用 break/continue。比如想遍历前 5 个元素,直接用 for (i in 0 until 5) 就好,没必要用 break。
4.8 综合示例:猜数字游戏
把今天学的知识串起来,写个小游戏:
import kotlin.random.Random
fun main() {
val target = Random.nextInt(1, 101) // 生成 1 到 100 的随机数
var guess: Int
var attempts = 0
println("猜数字游戏!范围 1 到 100。")
do {
print("请输入你的猜测:")
guess = readLine()?.toIntOrNull() ?: continue // 输入无效就重试
attempts++
when {
guess < target -> println("太小了,再试试")
guess > target -> println("太大了,再试试")
else -> println("恭喜你!猜对了!用了 $attempts 次。")
}
} while (guess != target)
}
这个例子用到了 do-while(至少猜一次)、when(多分支判断)、continue(跳过无效输入)。你看,分支和循环组合起来,就能写出有交互的程序了。
分支和循环是编程的基石。说白了,没有它们,程序就只能从头到尾直线执行,什么决策都做不了。我个人觉得,掌握好 if、when、for 这三个,就能解决 90% 的控制流问题。剩下的 while 和 break/continue,在特定场景下才会用到。
多写、多练,慢慢就会形成肌肉记忆。下次写代码时,遇到“要不要做”、“做哪个”、“做几次”这类问题,你自然就知道该用哪个了。