条件与循环
If 表达式
在 Kotlin 中,if
是一种表达式:它会返回一个值。 因此,Kotlin 没有三元操作符(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) {
// Not all cases are covered
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()) {
// No else branch is needed because all cases are covered
Bit.ZERO -> 0
Bit.ONE -> 1
}
为了简化
when
表达式并减少重复,请尝试上下文敏感解析(目前处于预览阶段)。 此特性允许你在when
表达式中,当已知预期类型时,在使用枚举条目或密封类成员时省略类型名称。关于更多信息,关于 上下文敏感解析的预览 或相关的 KEEP 提案 请参见。
如果你的 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")
。
For 循环
for
循环遍历任何提供迭代器的事物。这等同于 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
循环会被编译为基于索引的循环,而不会创建迭代器对象。
如果你想通过索引遍历数组或 list
,可以这样做:
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
操作符。关于更多信息,请参见 返回与跳转。