Kotlin编程实战:创建优雅、富于表现力和高性能的JVM与Android应用程序
上QQ阅读APP看书,第一时间看更新

4.3.1 when用作表达式

下面是一个函数实现,用来确定Conway’s Game of Life[1]中细胞是否将在下一代中存活。

这段代码告诉我们一个细胞是否会在下一代中存活,但是任何读到这段代码的程序员都可能很快失去活下去的动力,代码过于混乱、冗长、重复,而且容易出错。

在最简单的形式中,when可以替换一系列if-else语句或表达式。现在是使用Kotlin的when重构代码的好时机。

该函数的前一个版本指定了返回类型,并对方法体使用了块结构。在这个版本中,重构的代码使用了类型推断和单表达式函数语法。这里,when用作一个表达式。函数返回的值是when中的某一个分支返回的值。

两个版本的isAlive()函数对于相同的输入产生相同的结果,但是相比之下,使用when的函数产生的混乱较少。通常,与if相比,when更简洁,但作为一个细心的读者,你可以进一步重构上述代码,在=分隔符之后使用一个简单的alive && numberOfLiveNeighbors==2||numberOfLiveNeighbors==3来替换整个when表达式。

在when用作表达式的情况下,Kotlin编译器将验证else部分是否存在,或者该表达式是否将为所有可能的输入值生成一个值。这种编译时检查对代码的准确性有直接的影响,而且可以减少由于意外忽略了某些条件而导致的错误。

在前面的示例中,when表达式不接受任何参数,但是可以向它传递一个值或一个表达式。让我们看一个例子,在一个接受Any类型参数的函数中使用它,更多信息请参阅6.1.1节。

在下一个示例中,我们将使用Any来说明when的多种用途。有时,你可能会发现接收Any很有用。例如,在应用层中,消息代理可以接收不同类型的消息。将这些情况视为异常而不是常规,并且通常应避免将Any作为参数来定义方法。带着这些告诫,让我们来看看代码。

在本例中,我们将变量dayOfWeek传递给when。与此不同的是,上一个示例when中的每个条件都是布尔表达式,而在这个示例中,我们混合了多种条件。

when中的第一行检查给定的值是否为逗号分隔的值之一,不仅限于两个值。在接下来的两行中,我们分别检查给定的参数是列表的成员还是范围的成员。在"Friday"这行上,我们寻找精确匹配。除了匹配给定的值是否在列表或范围中之外,还可以执行类型检查,这就是我们在以is String开头的行中所做的。如果给定的输入与前面的任何条件都不匹配,并且类型为String,则执行该分支。最后,带有else的行处理默认情况。同样,在这种情况下,编译器将再次坚持使用else部分。

提醒一句,Kotlin编译器不允许else部分出现在任何地方,而只能作为when中的最后一个选项。但在更一般的条件下,例如,如果is String放在一个更具体的条件(比如"Friday")之前,则没有问题。条件的放置顺序很重要,执行将遵循与第一个满足的条件相对应的路径。

在我们目前所看到的示例中,->后面的代码是一个短的单表达式。Kotlin也允许这部分是一个块。块中的最后一个表达式成为执行该路径的结果。

尽管Kotlin允许在->之后是一个块,但从可读性的角度来看,也要避免这种情况。如果需要比单表达式或语句更复杂的逻辑,可将其重构为单独的函数或方法,并在->之后的路径中调用它。不要写又大、又丑的when表达式。

[1] https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life