Kotlin 为我们开发者提供了很多灵活的工具来控制程序的运转流程。开发者使用 if、when、循环来定义清晰易懂的逻辑。
If 表达式
Kotlin 的 if 使用方法,把条件添加到括号()
中,如果花括号是 true
,就把对应的执行写到花括号{}
中。对于其他的分支和检查,可以写道 else 和 else if 中。
开发者也可以把 if 作为表达式的形式,这样就可以把 if 的返回值赋值给变量。在这种用法下,else 分支是必须存在。 if 表达式的作用就是其他语言的三目运算:(condition ? then : else)
。
比如:
kotlin
var taller = heightAlice
if (heightAlice < heightBob) taller = heightBob
// Uses an else branch
if (heightAlice > heightBob) {
taller = heightAlice
} else {
taller = heightBob
}
// Uses if as an expression
taller = if (heightAlice > heightBob) heightAlice else heightBob
// Uses else if as an expression:
val heightLimit = 150
val heightOrLimit = if (heightLimit > heightAlice) heightLimit else if (heightAlice > heightBob) heightAlice else heightBob
println("Taller height is $taller")
// Taller height is 175
println("Height or limit is $heightOrLimit")
// Height or limit is 175
在 if 表达式中,每个分支都可以是一个代码块,代码块中最后一个表达式的值会成为该分支的返回结果:
kotlin
val heightAlice = 160
val heightBob = 175
val taller = if (heightAlice > heightBob) {
print("Choose Alice\n")
heightAlice
} else {
print("Choose Bob\n")
heightBob
}
println("Taller height is $taller")
When 表达式和语句
when 是基于多可能值或者多条件的条件表达式。和 Java 的 switch 语句比较相似。when 会计算它的参数,然后把结果和每个分支的条件进行比较,直到条件的结果和参数相符,比如:
kotlin
val userRole = "Editor"
when (userRole) {
"Viewer" -> print("User has read-only access")
"Editor" -> print("User can edit content")
else -> print("User role is not recognized")
}
// User can edit content
在 Kotlin 中,when 有两种使用方式:作为表达式或作为语句。
- 作为表达式时,when 会返回一个值,你可以在后续代码中使用这个值。
- 作为语句时,when 仅执行特定操作,不会返回结果。

when 既可以带主体(subject) 使用,也可以不带主体使用。这两种方式的运行逻辑相同,但带主体的写法通常会让代码更易读、更易维护,因为它能清晰地表明你要检查的对象是什么。

当你需要覆盖所有可能性的时候,才考虑使用 when。这种覆盖所有可能情况的要求,在 Kotlin 中被称为 "穷尽性"(Exhaustiveness)。
语句
如果将 when 用作语句,那么不需要覆盖所有可能的情况。在这种情况下,即使某些情况未被覆盖,也不会触发任何分支,也不会产生错误:
kotlin
val deliveryStatus = "OutForDelivery"
when (deliveryStatus) {
// Not all cases are covered
"Pending" -> print("Your order is being prepared")
"Shipped" -> print("Your order is on the way")
}
就像 if 一样,每一个分支可以是一个代码块,并且代码块最后一行表达式的值就是分支的值。
表达式
如果用 when 作为一个表达式,必须覆盖所有的可能性。第一个匹对上的符合条件的分支,就是整个 when 的值。如果没有覆盖所有的可能性,那么编译器会报错。
如果 when 表达式带着主体,那么可以使用 else 分支来确保能覆盖所有的可能性。比如,假如你的主体是个布尔、枚举、密封类或者它们的可空类型,就可以在不使用else的情况下,来覆盖所有的可能性:
kotlin
enum class Bit {
ZERO, ONE
}
fun getRandomBit(): Bit {
return if (Random.nextBoolean()) Bit.ONE else Bit.ZERO
}
fun main() {
val numericValue = when (getRandomBit()) {
// No else branch is needed because all cases are covered
Bit.ZERO -> 0
Bit.ONE -> 1
}
println("Random bit as number: $numericValue")
// Random bit as number: 0
如果 when 表达式没有主体,那么必须有一个 else 分支。这样没有满足条件的分支时,else 分支就会执行:
kotlin
val localFileSize = 1200
val remoteFileSize = 1200
val message = when {
localFileSize > remoteFileSize -> "Local file is larger than remote file"
localFileSize < remoteFileSize -> "Local file is smaller than remote file"
else -> "Local and remote files are the same size"
}
println(message)
// Local and remote files are the same size
when 的其他用法
when 语句和表达式为简化代码、处理多条件、执行类型检查提供了多种不同的方式。
使用逗号将多条件合并到一个分支:
kotlin
when (ticketPriority) {
"Low", "Medium" -> print("Standard response time")
else -> print("High-priority handling")
}
在 when 中,可以直接使用结果为布尔值(true/false)的表达式作为分支条件:
kotlin
when (enteredPin) {
// Expression
storedPin.toInt() -> print("PIN is correct")
else -> print("Incorrect PIN")
}
在 when 中,可以使用 in 关键字检查一个值是否包含在某个范围(range)或集合(collection)中,也可以使用 !in 检查值是否不包含在其中:
kotlin
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
使用 is 或者 !is 关键字来检查一个值的类型,由于智能转换,所以直接调用成员方法和属性:
kotlin
fun hasPrefix(input: Any): Boolean = when (input) {
is String -> input.startsWith("ID-")
else -> false
}
fun main() {
val testInput = "ID-98345"
println(hasPrefix(testInput))
// true
}
可以使用 when 来代替 if-else if 判断链。不带主体,分支条件是一个简洁的布尔表达式。第一个满足条件的分支就执行:
kotlin
val x = 5
val y = 8
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd")
}
// x is odd
最后,可以使用下面的语法将将when 主体(subject)捕获到一个变量中:
kotlin
fun main() {
val message = when (val input = "yes") {
"yes" -> "You said yes"
"no" -> "You said no"
else -> "Unrecognized input: $input"
}
println(message)
// You said yes
}
在 when 表达式或语句中声明的主体变量(即通过 when (val x = ...) 语法定义的变量),其作用域仅限于整个 when 的内部,在 when 外部无法访问该变量。
守护条件
守护条件允许在 when 表达式或语句的分支中包含多个条件,这就让复杂的控制流程更加清晰和简洁。只要 when 带有主体(subject),就可以使用守护条件。
在 when
的分支中,你可以将守护条件(guard condition) 放在主条件(primary condition) 之后,两者通过 if
连接。
kotlin
fun feedAnimal(animal: Animal) {
when (animal) {
// Branch with only primary condition
// Calls feedDog() when animal is Dog
is Animal.Dog -> feedDog()
// Branch with both primary and guard conditions
// Calls feedCat() when animal is Cat and not mouseHunter
is Animal.Cat if !animal.mouseHunter -> feedCat()
// Prints "Unknown animal" if none of the above conditions match
else -> println("Unknown animal")
}
}
fun main() {
val animals = listOf(
Animal.Dog("Beagle"),
Animal.Cat(mouseHunter = false),
Animal.Cat(mouseHunter = true)
)
animals.forEach { feedAnimal(it) }
// Feeding a dog
// Feeding a cat
// Unknown animal
}
多个条件使用逗号连接时,不能使用守护条件:
kotlin
0, 1 -> print("x == 0 or x == 1")
在同一个 when 表达式或语句中,可以同时包含带守护条件的分支和不带守护条件的分支。带守护条件的分支只有在主条件和守护条件都为 true 时才会执行;而如果主条件不匹配,对应的守护条件根本不会被计算(短路特性)。
由于 when 语句不需要覆盖所有可能的情况,在不带 else 分支的 when 语句中使用守护条件时,如果没有任何条件匹配(包括主条件和守护条件都不满足的情况),则不会执行任何代码。
与 when 语句不同,when 表达式必须覆盖所有可能的情况。如果在 when 表达式中使用守护条件且不添加 else 分支,编译器会强制要求处理每一种可能的情况,以避免运行时错误。
可以在单个分支中使用布尔运算符 &&(与)或 ||(或)组合多个守护条件。为避免混淆,建议用括号将布尔表达式括起来:
kotlin
when (animal) {
is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat()
}
守护条件同样支持 else if:
kotlin
when (animal) {
// Checks if `animal` is `Dog`
is Animal.Dog -> feedDog()
// Guard condition that checks if `animal` is `Cat` and not `mouseHunter`
is Animal.Cat if !animal.mouseHunter -> feedCat()
// Calls giveLettuce() if none of the above conditions match and animal.eatsPlants is true
else if animal.eatsPlants -> giveLettuce()
// Prints "Unknown animal" if none of the above conditions match
else -> println("Unknown animal")
}
For 循环
使用For 循环来迭代集合、数组、区间:
kotlin
for (item in collection) print(item)
循环体使用花括号包括{}:
kotlin
println("Things to buy:")
for (item in shoppingList) {
println("- $item")
}
// Things to buy:
// - Milk
// - Bananas
// - Bread
Ranges 区间
如果是迭代一个数字区间,那么可以使用区间表达式 .. 和 ..< 操作符:
kotlin
println("Closed-ended range:")
for (i in 1..6) {
print(i)
}
// Closed-ended range:
// 123456
println("\nOpen-ended range:")
for (i in 1..<6) {
print(i)
}
// Open-ended range:
// 12345
println("\nReverse order in steps of 2:")
for (i in 6 downTo 0 step 2) {
print(i)
}
// Reverse order in steps of 2:
// 6420
Arrays 数组
如果想要带着index迭代数组或者集合,可以使用indices属性:
kotlin
for (i in routineSteps.indices) {
println(routineSteps[i])
}
// Wake up
// Brush teeth
// Make coffee
并且,开发者也可以使用标准库中的.withIndex() 方法:
kotlin
for ((index, value) in routineSteps.withIndex()) {
println("The step at $index is \"$value\"")
}
// The step at 0 is "Wake up"
// The step at 1 is "Brush teeth"
// The step at 2 is "Make coffee"
Iterators 迭代器
for 循环可以迭代任何内置迭代器的对象。集合默认提供了迭代器,区间和数组会被编译为基于 index-based 的循环。
通过成员或者扩展方法调用iterator()来返回 Iterator<>,这样开发者可以自定义迭代器。iterator()必须包含:next() 和 hasNext();
继承 Iterable<T>
接口,实现iterator(), next(), 和 hasNext()方法,是自定义迭代器的最简单的方式:
koltin
class Booklet(val totalPages: Int) : Iterable<Int> {
override fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
var current = 1
override fun hasNext() = current <= totalPages
override fun next() = current++
}
}
}
fun main() {
val booklet = Booklet(3)
for (page in booklet) {
println("Reading page $page")
}
// Reading page 1
// Reading page 2
// Reading page 3
}
While 循环
while 和 do-while 循环会在条件满足时,持续的运行代码。二者的不同在于条件的检查时机:
-
while 的流程是:检查条件,执行代码,检查条件...
-
do-while 的流程是:执行代码,检查条件。条件满足的话,继续执行。因此,代码至少会执行一次,无视条件的情况。
while 循环的写法:条件写在圆括号,代码写在花括号:
kotlin
while (carsInGarage < maxCapacity) {
println("Car entered. Cars now in garage: ${++carsInGarage}")
}
// Car entered. Cars now in garage: 1
// Car entered. Cars now in garage: 2
// Car entered. Cars now in garage: 3
println("Garage is full!")
// Garage is full!
do-while循环的写法:代码写在花括号,判断的条件写在圆括号:
kotlin
do {
roll = Random.nextInt(1, 7)
println("Rolled a $roll")
} while (roll != 6)
// Rolled a 2
// Rolled a 6
println("Got a 6! Game over.")
// Got a 6! Game over.
二者只是执行顺序不一样。