当前位置: 首页 > news >正文

Kotlin编程学习记录2

Kotlin编程学习记录2——条件与循环

条件语句:ifwhen

​ Kotlin 的控制流把“表达式优先”作为设计原则——ifwhen 不只是控制语句,都可以作为表达式使用并返回值,这影响了日常代码风格(更函数式、可组合)。笔者这里影响深刻的是If的可返回表达式性,这个我在GNU C扩展语法中玩过,没想到在Kotlin中是一个非常标准的用法。

总体要点(速览)

  • if 可以用作表达式或语句:表达式时可直接返回值(因此 Kotlin 没有三元运算符)。
  • when 是比 switch 更强大的多分支构造:支持匹配常量、区间、类型检查、任意条件(无 subject 的 when),也能作为表达式返回值。when 分支不会“穿透”(不需要 break)。
  • when 用于 sealed class(或 sealed interface)时,编译器可以检测是否覆盖所有子类型,从而允许省略 else(exhaustive)。

if:声明、表达式、注意点

Kotlin的If跟大部分编程语言别无二致,我这种C++er看了都觉得回了家一般。
if (condition) {// statement block
} else {// statement block
}

​ 但是独特的是,当 if 用作表达式时,可以把值直接赋给变量或作为函数返回值:

val max = if (a > b) a else b    // 表达式形式
fun maxOf(a: Int, b: Int) = if (a > b) a else b

说明:Kotlin 因为有 if 表达式,所以没有三元运算符 ?:

​ 这就有点像:

var max = 0
if(a > b){max = a
}else{max = b
}

​ 但是你可以看到写法上Kotlin使用If直接返回值的写法显然简单的多。

表达式的类型规则

作为表达式时,if 的每个分支末尾表达式会被推断为某个类型。整个 if 表达式的类型是(各分支类型的)最近公共父类型或由编译器推断的最小上界(least upper bound)。如果某分支没有值(如最后是 Unit),则表达式类型会相应地调整

val x = if (flag) 1 else 2        // Int
val y = if (flag) "ok" else null  // String?(可空)

典型用法与陷阱

  • 优先用 if 表达式代替临时变量,代码更简洁、函数式风格更明显。
  • 不要误把 if 写成布尔表达式赋值:在有分支返回布尔值与其它副作用混合时,容易让类型变成 Boolean(见社区讨论“guarding against unintentional return types”)。谨慎检查每个分支返回的值类型。
  • if 支持嵌套与 block(块)形式,块内最后一行是该分支的结果(用于表达式场景)。

练习 1:使用 if/else 判断奇偶数

目标: 编写一个函数,它接受一个整数,然后返回一个字符串,告诉我们这个数是 “奇数” 还是 “偶数”。

要求:

  1. 创建一个名为 checkOddEven 的函数。
  2. 该函数接受一个 Int 类型的参数,名为 number
  3. 函数应返回一个 String 类型的结果。
  4. 使用 if/else 表达式来判断 number 是否能被 2 整除。
  5. 如果能,则返回 “偶数”;如果不能,则返回 “奇数”。

参考代码:

fun checkOddEven(number: Int): String {return if (number % 2 == 0) {"偶数"} else {"奇数"}
}fun main() {val num1 = 10val num2 = 7println("$num1${checkOddEven(num1)}") // 输出: 10 是 偶数println("$num2${checkOddEven(num2)}") // 输出: 7 是 奇数
}

笔记重点:

  • 在 Kotlin 中,if/else 是一个表达式 (expression),不是一个语句 (statement)。这意味着它可以有返回值。
  • 因此,我们可以直接使用 return if (...) 这样的简洁写法。
  • 代码块 {} 中最后一行就是该块的返回值。

when:强大且灵活的多分支工具

when 在 Kotlin 中是一个“一网打尽”的多用途分支表达式/语句:它既能完成传统 switch 的工作,也能做更复杂的匹配(范围、类型、任意条件),还可以作为表达式返回值。

基本语法(有 subject 与无 subject 两种形式)

有 subject 的形式(最常见)

when (x) {0 -> print("x == 0")1, 2 -> print("x == 1 or x == 2")  // 多个值使用逗号分隔in 3..5 -> print("x ∈ [3,5]")else -> print("x is something else")
}

无 subject 的形式(相当于一连串 if/else if

when {x.isEmpty() -> println("empty")x.length > 5 -> println("long")else -> println("other")
}
  • when 分支可以使用逗号把多个匹配值合并到同一个分支。

when 作为表达式

when 可以返回值并直接赋给变量或返回:

val result = when (x) {1 -> "one"2 -> "two"else -> "many"
}

注意:当 when 被用作表达式时,编译器要求覆盖所有可能情况或提供 else(除非 when 是对 sealed class 的完全覆盖情况)。

多种匹配条件支持(比 switch 更强)

每个 when 分支的条件可以是:

  • 常量(0, 'a', "s"
  • 多个常量(逗号分隔)
  • 区间或集合成员关系:in 1..10 / !in setOf(...)
  • 类型检查:is String / !is Int(结合智能转换 smart cast)
  • 任意表达式(在 无 subjectwhen 中尤其常见)
  • 函数调用的结果(在带 subject 的 when(x) 中,分支条件会与 subject 的值做等值比较 —— 例如 parseInt(s) -> 表示“当 subject 等于 parseInt(s) 时”)。
when (val v: Any = getValue()) {is String -> println(v.length) // v 被智能转换为 Stringis Int -> println(v + 1)      // v 是 Intelse -> println("unknown")
}

sealed classes 与可穷尽的 when

when 的 subject 类型是 sealed(或 sealed interface)的一个实例时,编译器能在编译期知道所有可能的子类型,从而检查 when 是否穷尽覆盖(exhaustive)。如果穷尽,则无需 else 分支;否则编译器会要求 else。这使得 when 在实现状态机或代数数据类型(ADT)匹配时非常强大。

sealed class Result {data class Success(val data: String): Result()object Failure: Result()
}fun handle(r: Result) = when (r) {is Result.Success -> println(r.data)Result.Failure -> println("failed")// 不需要 else(若覆盖了所有子类)
}

6. 复杂分支与多语句块

每个 when 的分支可以是单行表达式,也可以是一个块({ ... }),块的最后一行将作为该分支的返回值(当 when 作为表达式使用时)。这允许在分支中执行多步逻辑再返回最终值。(Kotlin)

val msg = when (cmd) {"ok" -> {log("ok received")"all good"}else -> "unknown"
}

练习 2:使用 when 表达式进行分数评级

目标: 编写一个函数,根据输入的分数(整数)给出对应的等级评价(字符串)。

要求:

  1. 创建一个名为 getGrade 的函数。
  2. 该函数接受一个 Int 类型的参数,名为 score
  3. 函数应返回一个 String 类型的结果。
  4. 使用 when 表达式实现以下评级逻辑:
    • score 为 100 时,返回 “满分!”
    • score 在 90 到 99 之间时,返回 “优秀”
    • score 在 75 到 89 之间时,返回 “良好”
    • score 在 60 到 74 之间时,返回 “及格”
    • 否则,返回 “不及格”

参考代码:

fun getGrade(score: Int): String {return when (score) {100 -> "满分!"in 90..99 -> "优秀"in 75..89 -> "良好"in 60..74 -> "及格"else -> "不及格"}
}fun main() {println("95分评价: ${getGrade(95)}")   // 输出: 95分评价: 优秀println("82分评价: ${getGrade(82)}")   // 输出: 82分评价: 良好println("60分评价: ${getGrade(60)}")   // 输出: 60分评价: 及格println("45分评价: ${getGrade(45)}")   // 输出: 45分评价: 不及格println("100分评价: ${getGrade(100)}") // 输出: 100分评价: 满分!
}

笔记重点:

  • when 是 Kotlin 中用于替代传统 switch 语句的更强大、更灵活的表达式。
  • 它可以匹配具体的值(如 100)。
  • 它可以用来检查值是否在一个区间内(如 in 90..99)。
  • else 分支用于处理所有其他未匹配的情况,类似于 switch 中的 default
  • if 一样,when 也是一个表达式,可以直接将其结果返回或赋值给变量。

Kotlin的循环语法

for 循环:更贴近 “foreach” 而非传统的 C/Java 风格

  • Kotlin 没有经典的三段式 for 遍历语法,而是更倾向函数式、可迭代结构:

    for (item in collection) { ... }
    

    适用于数组、集合、字符串、范围(只要实现了 Iterable

  • 常见用法:

    // Ranges
    for (i in 1..5) println(i)               // 1,2,3,4,5
    for (i in 5 downTo 1) println(i)         // 5,4,3,2,1
    for (i in 1..10 step 2) println(i)       // 1,3,5,7,9 :contentReference[oaicite:1]{index=1}// 数组、集合、字符串
    for (ch in "Kotlin") println(ch)
    for (elem in listOf(1,2,3)) println(elem) :contentReference[oaicite:2]{index=2}// 带索引迭代
    for (i in arr.indices) println("index $i = ${arr[i]}")
    for ((i, v) in arr.withIndex()) println("$i -> $v") :contentReference[oaicite:3]{index=3}
    

    whiledo-while:标准条件循环

while

  • 初次检查条件,若为 true 执行循环体。若初始条件为 false,则一次都不执行:

    while (condition) {// body
    }
    
    var i = 1
    while (i <= 5) {println(i)i++
    }
    

do-while

  • 循环体至少执行一次,之后再判断条件决定是否继续:

    do {// body
    } while (condition)
    

    常用场景:输入验证 / 重试逻辑等!


控制循环:breakcontinue

  • break:直接退出整个循环。
  • continue:跳过当前迭代,继续下一次。
for (i in 1..10) {if (i == 5) break       // 遇到 5 时退出循环if (i % 2 == 0) continue // 偶数跳过println(i)
}

防止无限循环的技巧与注意

  • while (true) 可用于无限循环,通常配合 break 结束:

    while (true) {// ...if (someCondition) break
    }
    

    这种写法虽常见,但要小心,确保终止条件有效

  • for 循环没有原生的无限循环形式,这也是 Kotlin 设计上摒弃经典 C/Java 风格循环的体现

快速对照总结

场景推荐用法说明
遍历集合、字符串、范围等for (x in ...)简洁、安全、函数式风格
条件循环while, do-while根据是否至少执行一次决定使用哪种
带索引遍历withIndex()indices获取下标或值更方便
跳出/跳过循环break, continue灵活控制循环流程
避免无限循环小心 while(true) 使用必须确保有跳出条件

练习 3:使用 for 循环打印九九乘法表

目标: 在控制台打印出一个经典、格式整齐的九九乘法表。

要求:

  1. 使用嵌套的 for 循环来完成。
  2. 外层循环控制乘法表的行(从 1 到 9)。
  3. 内层循环控制乘法表的列。关键点: 内层循环的次数应该依赖于外层循环当前的行数(例如,第 5 行只打印到 5 * 5)。
  4. 输出格式应为 “{乘数1} * {乘数2} = {结果}”,并且为了美观,每个算式之间用制表符 \t 分隔。
  5. 每打印完一行后,需要换行。

预期的输出效果:

1 * 1 = 1   
2 * 1 = 2   2 * 2 = 4   
3 * 1 = 3   3 * 2 = 6   3 * 3 = 9   
4 * 1 = 4   4 * 2 = 8   4 * 3 = 12  4 * 4 = 16  
... (以此类推,直到第9行)
9 * 1 = 9   9 * 2 = 18  ...          9 * 9 = 81  

提示:

  • 你可以使用 1..9 来创建一个从 1 到 9 的整数区间 (range)。
  • print() 函数用于在同一行打印,而 println() 函数会在打印后换行。
fun printMultiplicationTable() {// 外层循环控制行,从1到9for (row in 1..9) {// 内层循环控制列,从1到当前行数for (col in 1..row) {// 打印乘法表达式,并用 \t 补齐,使其对齐print("$col * $row = ${col * row}\t")}// 每行结束后,换行println()}
}fun main() {printMultiplicationTable()
}

笔记重点:

  • for (variable in range) 是 Kotlin 中最常见的循环形式。
  • 1..9 创建了一个闭区间,包含 1 和 9。
  • 嵌套循环是处理二维结构(如表格、矩阵)的常用方法。
  • print()println() 的区别使用是格式化输出的关键。

练习 4:使用 while 循环实现猜数字游戏

目标: 编写一个简单的猜数字游戏。程序会随机生成一个 1 到 100 之间的整数,然后让用户反复猜测,直到猜对为止。

要求:

  1. 在程序开始时,生成一个 1 到 100 之间的随机整数作为“谜底”。
  2. 使用 while 循环或 do-while 循环来不断获取用户的输入。
  3. 每次用户输入后,程序需要判断:
    • 如果用户猜的数字比谜底,则提示 “太大了,再猜!”。
    • 如果用户猜的数字比谜底,则提示 “太小了,再猜!”。
    • 如果用户猜对了,则提示 “恭喜你,猜对了!”,然后结束循环
  4. 记录用户总共猜了多少次,并在猜对后一并输出。

提示:

  • 生成随机数:可以使用 (1..100).random()
  • 读取用户输入:可以使用 readln()readLine()。注意它返回的是 String?,你可能需要用 toIntOrNull() 转换成整数并处理无效输入。
fun guessTheNumber() {// 1. 生成1到100的随机数作为谜底val secret = (1..100).random()var guessCount = 0var userGuess: Int? = nullprintln("我已经想好了一个 1 到 100 之间的数字,你来猜猜看!")// 2. 使用 while 循环,只要没猜对就一直继续while (userGuess != secret) {print("请输入你猜的数字: ")// 读取用户输入并转换为整数userGuess = readln().toIntOrNull()// 增加猜测次数guessCount++// 检查用户输入是否有效if (userGuess == null) {println("无效的输入,请输入一个数字!")continue // 跳过本次循环的剩余部分,直接开始下一次循环}// 3. 判断大小并给出提示if (userGuess > secret) {println("太大了,再猜!")} else if (userGuess < secret) {println("太小了,再猜!")}}// 4. 猜对后,循环结束,打印祝贺信息println("恭喜你,猜对了!谜底就是 $secret。")println("你总共猜了 $guessCount 次。")
}fun main() {guessTheNumber()
}

笔记重点:

  • while (condition) 循环会在条件为 true 时持续执行。它适用于循环次数不确定的场景。
  • do-while 循环(未在此示例中使用)与 while 类似,但它保证循环体至少执行一次
  • 从控制台读取用户输入是与用户交互的基础。
  • toIntOrNull() 是一个安全的字符串转整数方法,当转换失败时会返回 null,避免程序崩溃。
  • continue 关键字可以用来跳过当前循环的剩余代码,直接进入下一次迭代。

Reference

  • Kotlin 官方 — Control flow(if / when 等)。(Kotlin)
  • Kotlin 官方 — Basic syntax(说明 if 为表达式示例)。(Kotlin)
  • Kotlin 官方 — Sealed classes(when 与 sealed 的 exhaustiveness)。(Kotlin)
  • Baeldung — Guide to when(讲解 when 的常见变体与比较 switch)。(Baeldung on Kotlin)
  • GeeksforGeeks — Kotlin when expression 教程(多值、区间示例)。(GeeksforGeeks)
  • Kotlin 官方文档,比如控制流章节解读 whiledo-while(Kotlin),以及 for 循环和 ranges 介绍(programiz.com, Kotlin, GeeksforGeeks)。
  • 权威教程如 Baeldung、Programiz 的实战示例(Baeldung on Kotlin, programiz.com)。
  • 社区讨论强调设计理念与常见路径(Kotlin Discussions, Reddit)。
http://www.xdnf.cn/news/1445185.html

相关文章:

  • Linux `epoll` 机制的入口——`epoll_create`函数
  • 自由学习记录(92)
  • 图像正向扭曲反向扭曲
  • 关于缓存的一些思考?
  • 从Java全栈到前端框架:一次真实的面试对话
  • 自动化运维-ansible中对于大项目的管理
  • HTML第八课:HTML4和HTML5的区别
  • 网络通信与协议栈 -- OSI,TCP/IP模型,协议族,UDP编程
  • Linux 网络编程中核心函数`recv`。
  • Qt6用Chart模块做数据可视化?别再用老套路,看看这套35张图背后的秘密
  • MySQL :索引原理
  • 【面试题】BPE和WordPiece的区别?
  • Anaconda3出现Fatal error in launcher: Unable to create process using.....问题
  • STM32CubeMX + HAL 库:基于 I²C 通信的 BMP280气压海拔测量
  • 【超详细】别再看零散的教程了!一篇搞定Gitee从注册、配置到代码上传与管理(内含避坑指南最佳实践)
  • PS大神级AI建模技巧!效率翻倍工作流,悄悄收藏!
  • Wan系列模型解析--详细架构图
  • 机器学习在Backtrader多因子模型中的应用
  • 美团龙猫利用expat库实现的保存xml指定范围数据到csv的C程序
  • TypeScript 泛型入门(新手友好、完整详解)
  • XSENS VISION NAVIGATOR助力智能城市自动化清洁机器人精确导航
  • TLSF内存算法适配HTOS
  • 【Unity UGUI Canvas(画布)(1)】
  • 【音视频】FMP4 介绍
  • 【正点原子K210连载】第三十一章 音频FFT实验 摘自【正点原子】DNK210使用指南-CanMV版指南
  • 【论文阅读】-《THE JPEG STILL PICTURE COMPRESSION STANDARD》
  • Android 接入deepseek
  • 关于ES中文分词器analysis-ik快速安装
  • k8s使用StatefulSet(有状态)部署单节点 MySQL方案(使用本地存储)
  • 【Bug】Nexus无法正常启动的五种解决方法