控制流¶
Kotlin 的控制流包括 if、when、for、while、do-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 表示两件事都成立时才进入该分支:
- 主条件匹配:
animal是Animal.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指定步长。
while 与 do-while¶
var index = 0
while (index < 3) {
println(index)
index++
}
do-while 至少执行一次:
var input: String
do {
input = readln()
} while (input.isBlank())
跳转表达式¶
Kotlin 支持:
returnbreakcontinue
在嵌套 Lambda 中,标签返回很常见:
fun printNonEmpty(values: List<String>) {
values.forEach { value ->
if (value.isBlank()) return@forEach
println(value)
}
}
return@forEach 的意思是跳过当前 Lambda 调用,而不是从外层函数直接返回。
实践建议¶
- 简单赋值优先使用
if或when表达式,减少可变变量。 - 枚举、密封类配合
when时,尽量不写无意义的else,让编译器帮你检查新增分支。 - 带主语
when需要额外条件时,优先用 guard conditions,而不是在分支内再嵌套if。 - 需要索引和值时,优先
withIndex(),不要手写计数器。 - 复杂条件可以用无主语
when替代多层if-else。
参考¶
- 官方控制流:https://kotlinlang.org/docs/control-flow.html