1. 上一章:第2章
  2. 下一章:第4章
Kotlin:图解指南 • 第 3 章

Kotlin 条件语句:when 与 if

章节封面图片

在现实生活中,我们会根据不同情况做不同的事情。例如,如果外面下雨,我大概会带把伞。如果外面阳光明媚,我可能会戴上太阳镜。我会根据天气做不同的事情。

类似地,我们经常需要让代码在不同情况下做不同的事情。

在本章中,我们将全面学习 Kotlin 中的条件语句,它允许我们根据不同情况改变代码的运行方式!

金发姑娘与三个分支

你可能听说过金发姑娘与三只熊的童话故事。

熊爸爸、熊妈妈和熊宝宝住在树林中一栋可爱的小屋里。一天,三只熊准备好早餐粥后,决定出门悠闲地散散步。

与此同时,一个名叫金发姑娘的小女孩从窗户往里看,发现了三碗粥。由于她很饿,又看到没有人在家,她便决定进屋亲自品尝粥的味道。

  • 首先,她尝了熊爸爸的粥,惊呼:"太烫了!"
  • 然后她尝了熊妈妈的粥,抱怨道:"太冷了!"
  • 最后,她尝了熊宝宝的粥,发现温度刚刚好。"正合适!"她说,然后把整碗粥都吃完了。

让我们把金发姑娘对三碗粥的反应整理成一个简单的表格:

温度 金发姑娘的反应
大于 55°C ”太烫了!”
小于 40°C ”太冷了!”
其他情况 ”刚刚好!”

金发姑娘的反应因粥的温度不同而不同。

  1. 当温度高于 55°C 时,她说:"太烫了!"
  2. 当温度低于 40°C 时,她抱怨:"太冷了!"
  3. 但在两者之间时,她会说:"正合适!"

我们可以编写 Kotlin 代码,根据粥的温度告诉我们金发姑娘会说哪句话。用编程术语来说,上面三种不同情况——也就是表格中的三行——称为分支(branches)。如果有帮助的话,你可以把分支想象成树上的枝丫:

一棵树,有三个分支:太热、太冷和刚刚好。

到目前为止,当我们运行代码时,每一行代码都会执行。但现在,我们将开始编写每次只有一个分支会运行的代码。当代码运行时,所经过的路径——包括所走的特定分支——称为执行路径(execution path)。

在这里,你可以看到我们的 Kotlin 代码根据温度可能经过的三条执行路径

The same tree shown three times. Each one has an arrow running from the trunk to one branch.

根据某个条件(例如温度)在不同分支(例如金发姑娘的不同反应)之间做出选择的代码结构称为条件语句(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!"
}

为了展示它与表格的相似之处,我们稍微调整间距,把表格放到旁边对比。

Kotlin 中的 when 表达式,旁边有相同的表格。相同的代码见上面的代码清单 3.1。

如你所见,这看起来与上面的表格几乎完全相同!开括号 { 和闭括号 } 之间的每一行,就对应表格中的一行。

reaction 变量将根据 temperature 变量的值被赋予不同的值:

  • temperature 大于 55 时,reaction 将被赋值为 "It's too hot!"
  • temperature 小于 40 时,reaction 将被赋值为 "It's too cold!"
  • 否则,reaction 将被赋值为 "It's just right!"

为了真正理解 when,让我们看看它的各个组成部分:

The different parts of a 'when' expression, explained below.
  1. 首先是 when。这是一个关键字,告诉 Kotlin 你想根据不同情况做不同的事情。注意 when 是一个表达式,因此它会被求值并可以赋值给变量。在本例中,它将求值为一个字符串,并赋值给 reaction 变量。
  2. 接下来是 temperature > 55temperature < 40。这些称为条件(conditions)。条件是求值为 Boolean表达式

    temperature > 55 就是在问:" temperature 变量是否大于 55?"如果确实大于 55,则求值为 true,否则求值为 false。在 Kotlin 中比较数字时,可以使用大于号 > 或小于号 < 等。这些的专业术语是比较运算符。详见下方表格

  3. 当条件求值为 true 时,when 表达式将求值为箭头 -> 右侧的
  4. 在底部,我们有 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 的各个分支。

  1. 首先,temperature > 55 求值为 false,因为 -5 大于 55。
  2. 然后,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 只有两个可能的值——truefalse

Kotlin 知道 Boolean 值只能是 truefalse。因此,只要我们为这两种情况都添加了分支,就无需 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"

以下是各个组成部分:

An 'if' expression in Kotlin.

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 表达式!

whenif 用作语句

到目前为止,我们一直将 whenif 条件语句用作表达式,但你也可以将它们用作语句。以下是在 Kotlin 中使用 if 语句的示例:

var wattage = 0
var lightbulbIsOn = true

if (lightbulbIsOn) {
    wattage = wattage + 12
}

这里有几个有趣的注意事项:

  • 与条件表达式不同,条件语句不要求有 else 分支。在上面的示例中,如果 lightbulbIsOnfalse,则那个唯一的分支(wattage = wattage + 12)将永远不会运行。
  • 我们没有将这个 if 赋值给变量,因为如你所记,你不能将语句赋值给变量

总结

在短短三章中,你已经学到了很多关于 Kotlin 的知识!你了解了变量、类型、表达式、语句和函数。在本章中,你全面学习了条件语句,包括:

  • 什么是分支条件
  • 如何使用 when 表达式在不同情况下选取不同的值。
  • 当只有两个分支时,if 表达式如何成为好的选择。
  • 如何将 whenif 用作语句

在本书中,我们使用了各种类型,例如整数、字符串和 Boolean。在下一章中,我们将把变量函数组合在一起,使用对象来创建我们自己的类型。这些概念将为我们编写更高级的程序开辟许多激动人心的可能性,到时见!

感谢 David BlancRaheel NazMohit 对本章的审阅。


  1. 从 Kotlin 1.3 起,你还可以将主语捕获到一个变量中。 ↩︎