Kotlin編程學習記錄2——條件與循環
條件語句:if
與 when
? Kotlin 的控制流把“表達式優先”作為設計原則——if
、when
不只是控制語句,都可以作為表達式使用并返回值,這影響了日常代碼風格(更函數式、可組合)。筆者這里影響深刻的是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
判斷奇偶數
目標: 編寫一個函數,它接受一個整數,然后返回一個字符串,告訴我們這個數是 “奇數” 還是 “偶數”。
要求:
- 創建一個名為
checkOddEven
的函數。 - 該函數接受一個
Int
類型的參數,名為number
。 - 函數應返回一個
String
類型的結果。 - 使用
if/else
表達式來判斷number
是否能被 2 整除。 - 如果能,則返回 “偶數”;如果不能,則返回 “奇數”。
參考代碼:
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) - 任意表達式(在 無 subject 的
when
中尤其常見) - 函數調用的結果(在帶 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
表達式進行分數評級
目標: 編寫一個函數,根據輸入的分數(整數)給出對應的等級評價(字符串)。
要求:
- 創建一個名為
getGrade
的函數。 - 該函數接受一個
Int
類型的參數,名為score
。 - 函數應返回一個
String
類型的結果。 - 使用
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}
while
與do-while
:標準條件循環
while
-
初次檢查條件,若為
true
執行循環體。若初始條件為false
,則一次都不執行:while (condition) {// body }
var i = 1 while (i <= 5) {println(i)i++ }
do-while
-
循環體至少執行一次,之后再判斷條件決定是否繼續:
do {// body } while (condition)
常用場景:輸入驗證 / 重試邏輯等!
控制循環:break
、continue
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
循環打印九九乘法表
目標: 在控制臺打印出一個經典、格式整齊的九九乘法表。
要求:
- 使用嵌套的
for
循環來完成。 - 外層循環控制乘法表的行(從 1 到 9)。
- 內層循環控制乘法表的列。關鍵點: 內層循環的次數應該依賴于外層循環當前的行數(例如,第 5 行只打印到
5 * 5
)。 - 輸出格式應為
“{乘數1} * {乘數2} = {結果}”
,并且為了美觀,每個算式之間用制表符\t
分隔。 - 每打印完一行后,需要換行。
預期的輸出效果:
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 到 100 之間的隨機整數作為“謎底”。
- 使用
while
循環或do-while
循環來不斷獲取用戶的輸入。 - 每次用戶輸入后,程序需要判斷:
- 如果用戶猜的數字比謎底大,則提示 “太大了,再猜!”。
- 如果用戶猜的數字比謎底小,則提示 “太小了,再猜!”。
- 如果用戶猜對了,則提示 “恭喜你,猜對了!”,然后結束循環。
- 記錄用戶總共猜了多少次,并在猜對后一并輸出。
提示:
- 生成隨機數:可以使用
(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 官方文檔,比如控制流章節解讀
while
、do-while
(Kotlin),以及for
循環和 ranges 介紹(programiz.com, Kotlin, GeeksforGeeks)。 - 權威教程如 Baeldung、Programiz 的實戰示例(Baeldung on Kotlin, programiz.com)。
- 社區討論強調設計理念與常見路徑(Kotlin Discussions, Reddit)。