在现实生活中,我们会根据不同情况做不同的事情。例如,如果外面下雨,我大概会带把伞。如果外面阳光明媚,我可能会戴上太阳镜。我会根据天气做不同的事情。
类似地,我们经常需要让代码在不同情况下做不同的事情。
在本章中,我们将全面学习 Kotlin 中的条件语句,它允许我们根据不同情况改变代码的运行方式!
金发姑娘与三个分支
你可能听说过金发姑娘与三只熊的童话故事。
熊爸爸、熊妈妈和熊宝宝住在树林中一栋可爱的小屋里。一天,三只熊准备好早餐粥后,决定出门悠闲地散散步。
与此同时,一个名叫金发姑娘的小女孩从窗户往里看,发现了三碗粥。由于她很饿,又看到没有人在家,她便决定进屋亲自品尝粥的味道。
- 首先,她尝了熊爸爸的粥,惊呼:"太烫了!"
- 然后她尝了熊妈妈的粥,抱怨道:"太冷了!"
- 最后,她尝了熊宝宝的粥,发现温度刚刚好。"正合适!"她说,然后把整碗粥都吃完了。
让我们把金发姑娘对三碗粥的反应整理成一个简单的表格:
| 温度 | 金发姑娘的反应 |
|---|---|
| 大于 55°C | ”太烫了!” |
| 小于 40°C | ”太冷了!” |
| 其他情况 | ”刚刚好!” |
金发姑娘的反应因粥的温度不同而不同。
- 当温度高于 55°C 时,她说:"太烫了!"
- 当温度低于 40°C 时,她抱怨:"太冷了!"
- 但在两者之间时,她会说:"正合适!"
我们可以编写 Kotlin 代码,根据粥的温度告诉我们金发姑娘会说哪句话。用编程术语来说,上面三种不同情况——也就是表格中的三行——称为分支(branches)。如果有帮助的话,你可以把分支想象成树上的枝丫:
到目前为止,当我们运行代码时,每一行代码都会执行。但现在,我们将开始编写每次只有一个分支会运行的代码。当代码运行时,所经过的路径——包括所走的特定分支——称为执行路径(execution path)。
在这里,你可以看到我们的 Kotlin 代码根据温度可能经过的三条执行路径:
根据某个条件(例如温度)在不同分支(例如金发姑娘的不同反应)之间做出选择的代码结构称为条件语句(conditionals)。
那么,我们如何编写 Kotlin 代码来告诉我们金发姑娘对每碗粥会有什么反应呢?Kotlin 提供了几种条件语句供我们使用,来看看吧!
Kotlin 中 when 简介
让我们再来看看那张表格。
| 温度 | 金发姑娘的反应 |
|---|---|
| 大于 55°C | ”太烫了!” |
| 小于 40°C | ”太冷了!” |
| 其他情况 | ”刚刚好!” |
这样的表格易于阅读和理解。我们只需扫描左列,找到金发姑娘正在品尝的那碗粥的温度,就能在右列看到她的反应。
Kotlin 为我们提供了一个强大的条件语句 when,让我们能够在代码中编写与那张表格基本相同的内容。它是这样的:
val temperature = 48
val reaction = when {
temperature > 55 -> "It's too hot!"
temperature < 40 -> "It's too cold!"
else -> "It's just right!"
}为了展示它与表格的相似之处,我们稍微调整间距,把表格放到旁边对比。
如你所见,这看起来与上面的表格几乎完全相同!开括号 { 和闭括号 } 之间的每一行,就对应表格中的一行。
reaction 变量将根据 temperature 变量的值被赋予不同的值:
- 当
temperature大于 55 时,reaction将被赋值为"It's too hot!" - 当
temperature小于 40 时,reaction将被赋值为"It's too cold!" - 否则,
reaction将被赋值为"It's just right!"
为了真正理解 when,让我们看看它的各个组成部分:
- 首先是
when。这是一个关键字,告诉 Kotlin 你想根据不同情况做不同的事情。注意when是一个表达式,因此它会被求值并可以赋值给变量。在本例中,它将求值为一个字符串,并赋值给reaction变量。 - 接下来是
temperature > 55和temperature < 40。这些称为条件(conditions)。条件是求值为 Boolean 的表达式。temperature > 55就是在问:"temperature变量是否大于 55?"如果确实大于 55,则求值为true,否则求值为false。在 Kotlin 中比较数字时,可以使用大于号>或小于号<等。这些的专业术语是比较运算符。详见下方表格。 - 当条件求值为
true时,when表达式将求值为箭头 右侧的值。-> - 在底部,我们有
else,它是一个兜底分支,当上面所有条件都不匹配时使用。
when 表达式非常有用,你可能会经常用到它。它还有一些有趣的特性,让我们来看几个。
第一个为真的条件获胜
金发姑娘刚刚在冰箱里又发现了一些粥,所以让我们再添加一种极冷温度的情况:
val temperature = -5
val reaction = when {
temperature > 55 -> "It's too hot!"
temperature < 40 -> "It's too cold!"
temperature < 0 -> "It's frigid!"
else -> "It's just right!"
}在这段代码中,由于 temperature 变量被设置为 -5,你可能期望 reaction 被设置为 "It's frigid!",但实际上它会被设置为 "It's too cold!"。
这是为什么呢?
因为 Kotlin 从上到下依次检查 when 的各个分支。
- 首先,
temperature > 55求值为false,因为 -5 不大于 55。 - 然后,
temperature < 40求值为true,因为 -5 确实小于 40。
此时,Kotlin 使用 "It's too cold!",因为它找到了一个匹配的条件。因此,第三种情况 temperature < 0 永远不会被求值。
换句话说,第一个求值为 true 的条件获胜。
要确保在温度低于零时使用 "It's frigid" 很容易。只需将该情况放在温度小于 40 度的条件之前即可,如下所示:
val temperature = -5
val reaction = when {
temperature > 55 -> "It's too hot!"
temperature < 0 -> "It's frigid!"
temperature < 40 -> "It's too cold!"
else -> "It's just right!"
}现在,当你运行这段代码时,temperature < 0 的情况将在 temperature < 40 之前运行,所以当温度为 -5 时,reaction 将是 "It's frigid!"。
精确匹配
有时,我们不需要检查一个范围的值(例如"大于 55 的任何值"或"小于 40 的任何值"),而只需要检查一个精确匹配。
三只熊发现金发姑娘非常喜欢他们的粥之后,决定编写一本包含所有家族食谱的烹饪书。他们开设了一家网店并开始销售。由于书非常受欢迎,他们希望为购买多册的顾客提供折扣。
以下是折扣方案:
| 订购数量 | 每本价格 |
|---|---|
| 1本 | $19.99 |
| 2本 | $18.99 |
| 3本 | $16.99 |
| 4本 | $16.99 |
| 5本或更多书籍 | $14.99 |
我们可以像这样编写 when 语句:
val quantity = 3
val pricePerBook = when {
quantity == 1 -> 19.99
quantity == 2 -> 18.99
quantity == 3 -> 16.99
quantity == 4 -> 16.99
else -> 14.99
}然而,Kotlin 允许你通过指定一个主语(subject)来进一步简化。它是这样的:
val quantity = 3
val pricePerBook = when (quantity) {
1 -> 19.99
2 -> 18.99
3 -> 16.99
4 -> 16.99
else -> 14.99
}在这个示例中,quantity 称为 when 表达式的主语。主语可以是任何表达式1,但这里我们只使用了变量 quantity。因为以 quantity 作为主语,就不必在每个条件中写 quantity == 了!
Kotlin 还为我们提供了一个快捷方式,让代码更加简洁。由于购买 3 本书和 4 本书的单价相同,我们可以将这两个条件放在同一行,用逗号分隔,如下所示:
val quantity = 3
val pricePerBook = when (quantity) {
1 -> 19.99
2 -> 18.99
3, 4 -> 16.99
else -> 14.99
}必须覆盖所有可能的条件
Kotlin 中的 when 表达式必须包含对所有可能值的条件处理。因此,我们说 when 表达式必须是穷举的(exhaustive)。这也是为什么通常需要在底部加上 else 条件的原因。
有时即使没有 else 条件,你也可以做到穷举。例如,考虑一个 Boolean 表达式。如你所知,Boolean 只有两个可能的值——true 和 false。
Kotlin 知道 Boolean 值只能是 true 或 false。因此,只要我们为这两种情况都添加了分支,就无需 else。
val isLightbulbOn = true
val reaction = when (isLightbulbOn) {
true -> "It's bright"
false -> "I can't see"
}这不是唯一可以省略 else 条件的情况。当我们学习枚举类和密封类型时,还会再次见到这种情况。
when 表达式很强大,作为 Kotlin 开发者,你会经常用到它!但对于像上面灯泡示例这样的简单 Boolean 检查,它可能有点"大材小用"。对于这些情况,Kotlin 为我们提供了第二种条件语句。
if 表达式
我们可以使用 if 表达式来使灯泡示例更加简洁,而不是使用 when 表达式。if 表达式类似于 when 表达式,但它只有一个单一条件和两个分支——一个用于 true,一个用于 false。
它是这样的:
val reaction = if (isLightbulbOn) "It's bright" else "I can't see"以下是各个组成部分:
if 是一个关键字,与 when 类似。isLightbulbOn 是被求值的条件。
- 如果求值为
true,则reaction将被赋值为"It's bright" - 如果求值为
false,则reaction将被赋值为"I can't see"
如果你愿意,可以将分支格式化在不同的行上。例如:
val reaction =
if (isLightbulbOn)
"It's bright"
else
"I can't see"你也可以在分支周围使用大括号 { 和 }。这样可以在分支中添加语句,如下所示:
var isLightbulbOn = true
val reaction =
if (isLightbulbOn) {
isLightbulbOn = false
"I just turned the light off."
} else {
isLightbulbOn = true
"I just turned the light on."
}通常,作为 Kotlin 程序员,如果只有一个条件需要检查,你使用 if 表达式;如果有多个条件需要检查,你使用 when 表达式。
使用 if 处理多个条件在技术上是可行的。例如,还记得清单 3.5 中的图书单价查询吗?我们可以将其改写为 if 表达式,如下所示:
val pricePerBook =
if (quantity == 1)
19.99
else if (quantity == 2)
18.99
else if (quantity == 3)
16.99
else if (quantity == 4)
16.99
else
14.99如果你发现自己这样做,停下来!
请改用 when 表达式!
when 与 if 用作语句
到目前为止,我们一直将 when 和 if 条件语句用作表达式,但你也可以将它们用作语句。以下是在 Kotlin 中使用 if 语句的示例:
var wattage = 0
var lightbulbIsOn = true
if (lightbulbIsOn) {
wattage = wattage + 12
}这里有几个有趣的注意事项:
- 与条件表达式不同,条件语句不要求有
else分支。在上面的示例中,如果lightbulbIsOn为false,则那个唯一的分支(wattage = wattage + 12)将永远不会运行。 - 我们没有将这个
if赋值给变量,因为如你所记,你不能将语句赋值给变量。
总结
在短短三章中,你已经学到了很多关于 Kotlin 的知识!你了解了变量、类型、表达式、语句和函数。在本章中,你全面学习了条件语句,包括:
在本书中,我们使用了各种类型,例如整数、字符串和 Boolean。在下一章中,我们将把变量和函数组合在一起,使用类和对象来创建我们自己的类型。这些概念将为我们编写更高级的程序开辟许多激动人心的可能性,到时见!
感谢 David Blanc、Raheel Naz 和 Mohit 对本章的审阅。
-
从 Kotlin 1.3 起,你还可以将主语捕获到一个变量中。 ↩︎