条件与循环
If 表达式
在 Kotlin 中,if
是一个表达式:它会返回一个值。 因此,不存在三元运算符(condition ? then : else
),因为普通的 if
在此作用下工作得很好。
fun main() {
val a = 2
val b = 3
var max = a
if (a < b) max = b
// With else
if (a > b) {
max = a
} else {
max = b
}
// As expression
max = if (a > b) a else b
// You can also use `else if` in expressions:
val maxLimit = 1
val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b
println("max is $max")
// max is 3
println("maxOrLimit is $maxOrLimit")
// maxOrLimit is 3
}
if
表达式的分支可以是代码块。在这种情况下,最后一个表达式就是该代码块的值:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
如果你将 if
作为表达式使用,例如,用于返回其值或将其赋值给变量,则 else
分支是强制性的。
When 表达式和语句
when
是一个条件表达式,它根据多个可能的值或条件来运行代码。它类似于 Java、C 和其他类似语言中的 switch
语句。例如:
fun main() {
val x = 2
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> print("x is neither 1 nor 2")
}
// x == 2
}
when
会按顺序将其参数与所有分支进行匹配,直到某个分支条件被满足为止。
你可以使用 when
的几种不同方式。首先,你可以将 when
用作表达式或语句。 作为表达式,when
返回一个值,供你的代码后续使用。作为语句,when
完成一个动作,不返回任何进一步有用的值:
表达式 | 语句 |
kotlin
| kotlin
|
其次,你可以有主题(subject)地或无主题地使用 when
。无论你是否将主题与 when
一起使用,你的表达式或语句行为相同。我们建议尽可能使用带主题的 when
,因为它通过清晰地展示你正在检查的内容,使你的代码更易于阅读和维护。
带主题 x | 无主题 |
kotlin
| kotlin
|
根据你使用 when
的方式,对于你是否需要在分支中覆盖所有可能的情况,有不同的要求。
如果你将 when
用作语句,你不必覆盖所有可能的情况。在此示例中,某些情况未被覆盖,因此什么也不会发生。然而,不会发生错误:
fun main() {
val x = 3
when (x) {
// 并非所有情况都已覆盖
1 -> print("x == 1")
2 -> print("x == 2")
}
}
在 when
语句中,各个分支的值会被忽略。就像 if
一样,每个分支都可以是一个代码块,并且它的值是该代码块中最后一个表达式的值。
如果你将 when
用作表达式,你必须覆盖所有可能的情况。换句话说,它必须是 穷尽的。 第一个匹配分支的值成为整个表达式的值。如果你不覆盖所有情况,编译器会抛出错误。
如果你的 when
表达式有主题,你可以使用 else
分支来确保覆盖所有可能的情况,但这并不是强制性的。例如,如果你的主题是 Boolean
、enum
类、sealed
类,或它们的对应可空类型之一,你可以不使用 else
分支就覆盖所有情况:
enum class Bit {
ZERO, ONE
}
val numericValue = when (getRandomBit()) {
// 不需要 else 分支,因为所有情况都已覆盖
Bit.ZERO -> 0
Bit.ONE -> 1
}
如果你的 when
表达式没有主题,你必须有一个 else
分支,否则编译器会抛出错误。 当其他分支条件均未满足时,else
分支会被评估:
val message = when {
a > b -> "a is greater than b"
a < b -> "a is less than b"
else -> "a is equal to b"
}
when
表达式和语句提供了不同的方式来简化你的代码、处理多个条件并执行类型检查。
你可以通过在一行中使用逗号组合多个情况的条件来定义它们的共同行为:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
你可以使用任意表达式(不限于常量)作为分支条件:
when (x) {
s.toInt() -> print("s encodes x")
else -> print("s does not encode x")
}
你还可以通过 in
或 !in
关键字检查一个值是否包含在区间或集合中:
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
关键字检查一个值是否为特定类型。请注意,由于智能类型转换,你可以访问该类型的成员函数和属性,而无需任何额外的检查。
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
你可以使用 when
来替代 if
-else
if
链。 如果没有主题,分支条件就是简单的布尔表达式。第一个条件为 true
的分支会运行:
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd")
}
你可以通过以下语法将主题捕获到一个变量中:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
作为主题引入的变量的作用域仅限于 when
表达式或语句的主体。
when 表达式中的守卫条件
守卫条件允许你在 when
表达式的分支中包含多个条件,使复杂的控制流更明确和简洁。 你可以在带主题的 when
表达式或语句中使用守卫条件。
要在分支中包含守卫条件,请将其放在主条件之后,用 if
分隔:
sealed interface Animal {
data class Cat(val mouseHunter: Boolean) : Animal
data class Dog(val breed: String) : Animal
}
fun feedAnimal(animal: Animal) {
when (animal) {
// 仅包含主条件的分支。当 `animal` 是 `Dog` 时调用 `feedDog()`。
is Animal.Dog -> feedDog()
// 包含主条件和守卫条件的分支。当 `animal` 是 `Cat` 且不是 `mouseHunter` 时调用 `feedCat()`。
is Animal.Cat if !animal.mouseHunter -> feedCat()
// 如果上述条件都不匹配,则打印 "Unknown animal"
else -> println("Unknown animal")
}
}
在单个 when
表达式中,你可以结合使用带守卫条件和不带守卫条件的分支。 带有守卫条件的分支中的代码仅在主条件和守卫条件都评估为 true 时运行。 如果主条件不匹配,守卫条件将不会被评估。
如果你在没有 else
分支的 when
语句中使用守卫条件,并且没有任何条件匹配,则不会执行任何分支。
否则,如果你在没有 else
分支的 when
表达式中使用守卫条件,编译器会要求你声明所有可能的情况,以避免运行时错误。
此外,守卫条件支持 else if
:
when (animal) {
// 检查 `animal` 是否为 `Dog`
is Animal.Dog -> feedDog()
// 守卫条件,检查 `animal` 是否为 `Cat` 且不是 `mouseHunter`
is Animal.Cat if !animal.mouseHunter -> feedCat()
// 如果上述条件都不匹配且 animal.eatsPlants 为 true,则调用 giveLettuce()
else if animal.eatsPlants -> giveLettuce()
// 如果上述条件都不匹配,则打印 "Unknown animal"
else -> println("Unknown animal")
}
在单个分支中组合多个守卫条件,请使用布尔运算符 &&
(AND) 或 ||
(OR)。 在布尔表达式周围使用括号以避免混淆:
when (animal) {
is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat()
}
你可以在任何带主题的 when
表达式或语句中使用守卫条件,除了有多个条件用逗号分隔的情况。 例如,0, 1 -> print("x == 0 or x == 1")
。
NOTE
要在 CLI 中启用守卫条件,请运行以下命令:
kotlinc -Xwhen-guards main.kt
要在 Gradle 中启用守卫条件,请将以下行添加到 build.gradle.kts
文件中:
kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")
For 循环
for
循环迭代任何提供迭代器(iterator)的事物。这等同于 C# 等语言中的 foreach
循环。 for
的语法如下:
for (item in collection) print(item)
for
的主体可以是一个代码块。
for (item: Int in ints) {
// ...
}
如前所述,for
迭代任何提供迭代器的事物。这意味着它:
- 具有一个成员函数或扩展函数
iterator()
,它返回Iterator<T>
,并且:- 具有一个成员函数或扩展函数
next()
- 具有一个成员函数或扩展函数
hasNext()
,它返回Boolean
。
- 具有一个成员函数或扩展函数
这三个函数都需要被标记为 operator
。
要迭代数字范围,请使用区间表达式:
fun main() {
for (i in 1..3) {
print(i)
}
for (i in 6 downTo 0 step 2) {
print(i)
}
// 1236420
}
针对区间或数组的 for
循环会被编译成基于索引的循环,不会创建迭代器对象。
如果你想带索引迭代数组或列表,你可以这样做:
fun main() {
val array = arrayOf("a", "b", "c")
for (i in array.indices) {
print(array[i])
}
// abc
}
另外,你可以使用 withIndex
库函数:
fun main() {
val array = arrayOf("a", "b", "c")
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
// the element at 0 is a
// the element at 1 is b
// the element at 2 is c
}
While 循环
while
和 do-while
循环会持续处理其主体,只要它们的条件满足。 它们之间的区别在于条件检查的时间:
while
会先检查条件,如果条件满足,则处理循环体,然后返回到条件检查。do-while
会先处理循环体,然后检查条件。如果条件满足,循环会重复。因此,无论条件如何,do-while
的循环体至少会运行一次。
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y 在此处可见!
循环中的 Break 和 Continue
Kotlin 支持循环中的传统 break
和 continue
运算符。详见返回与跳转。