Kotlin与Java虚拟机三:控制流

第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@标签 跳过当前迭代。

动手练习

  1. 写一个函数,输入分数(0--100),用 when 返回等级:90+ 优秀,80--89 良好,60--79 及格,其余不及格。
  2. 用嵌套循环打印九九乘法表,要求当乘积超过 20 时用标签跳出外层循环。
  3. 写一段代码遍历 1..100,用 continue 跳过所有奇数,只打印偶数。

相关推荐
benchmark_cc13 分钟前
单只股票五档盘口接入个人研究仓库:QuantDash 读取、校验与易错点
python·数据分析·pandas·量化交易·股票数据·quantdash
石小石Orz13 分钟前
AI 正在蚕食我们的编码能力
前端·后端·算法
xiaoye-duck24 分钟前
《C++ 项目:手写大模型接入 SDK》Ollama接口设计(上):初始化、全量响应实现和测试
开发语言·网络·c++·语言模型
NE_STOP44 分钟前
Spring AI Alibaba - MCP入门
java
nice_lcj5201 小时前
【从零理解 Spring AI · 07】对话记忆:让模型记住上一句说了什么
java·spring ai·ai应用开发·chatmemory·对话记忆
陈随易1 小时前
专注飞机 FocusFlight,两个人把番茄钟做成了一趟航班
前端·后端·程序员
量化分析码农1 小时前
【Python量化波动率实战 04】低波动异象:低波组真的跑赢高波组吗
后端
疯华整猫1 小时前
我用 Trae 把 Android 内存账重算了一遍
android·性能优化·源码
风上猫1 小时前
MySQL多值索引:让 JSON 数组也能走索引(以及 6 个容易踩的坑)
后端