when
表达式
当一个模式匹配时,执行相应的操作是计算机编程中的重要部分。
任何简化此任务的工具对程序员来说都是一种福音。当需要做出超过两三个选择时,when表达式比if
表达式更加方便。
when
表达式用于将一个值与一组可能性进行比较。它以关键字when
和要比较的括号内的值开始。接下来是一个包含一组可能的匹配和它们关联的操作的主体。每个匹配都是一个表达式,后面跟着一个右箭头->
。箭头是两个分开的字符-
和>
,之间没有空白字符。该表达式会被求值并与目标值进行比较。如果匹配成功,则->
右侧的表达式将成为when
表达式的结果。
下面的示例中,ordinal()
函数根据基数数词构建德语序数词。它将整数与固定的一组数进行匹配,以检查它们是否适用于一般规则或是例外情况(在德语中这种情况非常常见):
// WhenExpressions/GermanOrdinals.kt
package whenexpressions
import atomictest.eq
val numbers = mapOf(
1 to "eins", 2 to "zwei", 3 to "drei",
4 to "vier", 5 to "fuenf", 6 to "sechs",
7 to "sieben", 8 to "acht", 9 to "neun",
10 to "zehn", 11 to "elf", 12 to "zwoelf",
13 to "dreizehn", 14 to "vierzehn",
15 to "fuenfzehn", 16 to "sechzehn",
17 to "siebzehn", 18 to "achtzehn",
19 to "neunzehn", 20 to "zwanzig"
)
fun ordinal(i: Int): String =
when (i) { // [1]
1 -> "erste" // [2]
3 -> "dritte"
7 -> "siebte"
8 -> "achte"
20 -> "zwanzigste"
else -> numbers.getValue(i) + "te" // [3]
}
fun main() {
ordinal(2) eq "zweite"
ordinal(3) eq "dritte"
ordinal(11) eq "elfte"
}
- [1]
when
表达式将i
与主体中的匹配表达式进行比较。 - [2] 第一个匹配成功的表达式将完成
when
表达式的执行,这里会产生一个String
,成为ordinal()
的返回值。 - [3]
else
关键字表示当没有匹配时会“穿透”。
else
分支始终出现在匹配列表的最后。当我们对2
进行测试时,它不与1、3、7、8或20匹配,因此会进入else
分支。
如果在上面的示例中忘记添加else
分支,编译时会出现错误:“‘when’表达式必须是穷尽的,请添加必要的‘else’分支”。如果将when
表达式视为语句(即不使用when
的结果),可以省略else
分支。此时未匹配的值将被忽略。
在下面的示例中,Coordinates
类使用属性访问器报告其属性的更改。when
表达式处理inputs
列表中的每个项目:
// WhenExpressions/AnalyzeInput.kt
package whenexpressions
import atomictest.*
class Coordinates {
var x: Int = 0
set(value) {
trace("x gets $value")
field = value
}
var y: Int = 0
set(value) {
trace("y gets $value")
field = value
}
override fun toString() = "($x, $y)"
}
fun processInputs(inputs: List<String>) {
val coordinates = Coordinates()
for (input in inputs) {
when (input) { // [1]
"up", "u" -> coordinates.y-- // [2]
"down", "d" -> coordinates.y++
"left", "l" -> coordinates.x--
"right", "r" -> { // [3]
trace("Moving right")
coordinates.x++
}
"nowhere" -> {} // [4]
"exit" -> return // [5]
else -> trace("bad input: $input")
}
}
}
fun main() {
processInputs(listOf("up", "d", "nowhere",
"left", "right", "exit", "r"))
trace eq """
y gets -1
y gets 0
x gets -1
Moving right
x gets 0
"""
}
- [1]
input
与不同的选项进行匹配。 - [2] 可以使用逗号在一个分支中列出多个值。这里,如果用户输入的是“up”或“u”,我们将其解释为向上移动。
- [3] 一个分支内的多个动作必须位于一个代码块中。
- [4] 使用空代码块表示“什么都不做”。
- [5] 在一个分支中从外部函数返回是有效的操作。在这里,
return
终止对processInputs()
的调用。
when
表达式的参数可以是任何表达式,并且匹配可以是任何值(不仅限于常量):
// WhenExpressions/MatchingAgainstVals.kt
import atomictest.*
fun main() {
val yes = "A"
val no = "B"
for (choice in listOf(yes, no, yes)) {
when (choice) {
yes -> trace("Hooray!")
no -> trace("Too bad!")
}
// 使用 'if' 相同的逻辑:
if (choice == yes) trace("Hooray!")
else if (choice == no) trace("Too bad!")
}
trace eq """
Hooray!
Hooray!
Too bad!
Too bad!
Hooray!
Hooray!
"""
}
when
表达式可以覆盖if
表达式的功能。由于when
更灵活,因此在有选择的情况下更推荐使用它。
我们可以将一个值的Set
与另一个值的Set
进行匹配:
// WhenExpressions/MixColors.kt
package whenexpressions
import atomictest.eq
fun mixColors(first: String, second: String) =
when (setOf(first, second)) {
setOf("red", "blue") -> "purple"
setOf("red", "yellow") -> "orange"
setOf("blue", "yellow") -> "green"
else -> "unknown"
}
fun main() {
mixColors("red", "blue") eq "purple"
mixColors("blue", "red") eq "purple"
mixColors("blue", "purple") eq "unknown"
}
在mixColors()
中,我们使用Set
作为when
的参数,并将其与不同的Set
进行比较。我们使用Set
是因为元素的顺序是不重要的,当我们混合“red”和“blue”与混合“blue”和“red”时,我们需要相同的结果。
when
有一种特殊的形式,它不带参数。省略参数意味着分支可以检查不同的布尔条件。您可以使用任何布尔表达式作为分支条件。例如,我们重新编写了Number Types中介绍的bmiMetric()
,首先显示原始解决方案,然后使用when
替代if
:
// WhenExpressions/BmiWhen.kt
package whenexpressions
import atomictest.eq
fun bmiMetricOld(
kg: Double,
heightM: Double
): String {
val bmi = kg / (heightM * heightM)
return if (bmi < 18.5) "Underweight"
else if (bmi < 25) "Normal weight"
else "Overweight"
}
fun bmiMetricWithWhen(
kg: Double,
heightM: Double
): String {
val bmi = kg / (heightM * heightM)
return when {
bmi < 18.5 -> "Underweight"
bmi < 25 -> "Normal weight"
else -> "Overweight"
}
}
fun main() {
bmiMetricOld(72.57, 1.727) eq
bmiMetricWithWhen(72.57, 1.727)
}
使用when
的解决方案是选择多个选项之间的更优雅的方式。
练习和解答可以在 www.AtomicKotlin.com 上找到。