《Kotlin核心编程》 ——2.4.4 枚举类和when表达式

举报
华章计算机 发表于 2020/02/21 21:02:03 2020/02/21
【摘要】 本节书摘来自华章计算机《Kotlin核心编程》 —— 书中第2章,第2.4.4节,作者是水滴技术团队 。

2.4.4 枚举类和when表达式

本节主要介绍Kotlin中另一种非常强大的表达式—when表达式。在了解它之前,我们先来看看在Kotlin中如何定义枚举结构,然后再使用when结合枚举更好地来设计业务,并介绍when表达式的具体语法。

1.枚举是类

在Kotlin中,枚举是通过一个枚举类来实现的。先来实现一个很简单的例子:

enum class Day {

    MON,

    TUE,

    WEN,

    THU,

    FRI,

    SAT,

    SUN

}

与Java中的enum语法大体相似,无非多了一个class关键词,表示它是一个枚举类。不过Kotlin中的枚举类当然没那么简单,由于它是一种类,我们可以猜测它自然应该可以拥有构造参数,以及定义额外的属性和方法。

enum class DayOfWeek(val day: Int) {

    MON(1),

    TUE(2),

    WEN(3),

    THU(4),

    FRI(5),

    SAT(6),

    SUN(7)

    ;  // 如果以下有额外的方法或属性定义,则必须强制加上分号

 

    fun getDayNumber(): Int {

        return day

    }

}

需要注意的是,当在枚举类中存在额外的方法或属性定义,则必须强制加上分号,虽然你很可能不会喜欢这个语法。

枚举类“分号”语法的由来

早期枚举类的语法并没有逗号,然而却有点烦琐:

  enum class DayOfWeek(val day: Int) {

      MON: DayOfWeek(1)

      TUE: DayOfWeek(2)

      WEN: DayOfWeek(3)

      THU: DayOfWeek(4)

      FRI: DayOfWeek(5)

      SAT: DayOfWeek(6)

      SUN: DayOfWeek(7)

  }

每个枚举值都需要通过DayOfWeek(n)来构造,这确实显得多余。理想的情况是我们只需调用MON(1)来表示即可。然而,简化语法后也带来了一些技术上的问题,比如在枚举类源码实现上很难把具体的枚举值与类方法进行区分。解决这个问题有好几种思路,第一种办法就是把每个方法都加上一个注解前缀,例如:

  @inject fun getDayNumber(): Int {

      return day

  }

但是这样子就与其他的类在语法上显得不一样,破坏了语法的一致性。好吧,那么能不能反过来,给每个枚举类弄个关键词前缀来区分,比如:

  entry MON(1)

显然,这样也不好。因为枚举值的数量无法控制,如果数量较多,会显得啰唆。Kotlin最终采用了引入逗号和分号的语法,即通过逗号对每个枚举值进行分隔,这样就可以最终采用一个分号来对额外定义的属性和方法进行隔离。

这确实是相对更合理的设计方案,尤其是加上逗号之后,Kotlin中的枚举类语法跟Java的枚举更相似了,这符合Kotlin的设计原则。

2.用when来代替if-else

在了解如何声明一个枚举类后,我们再来用它设计一个业务逻辑。比如,Shaw给新一周的几天计划了不同的活动,安排如下:

周六打篮球

周日钓鱼

星期五晚上约会

平日里如果天晴就去图书馆看书,不然就在寝室学习

他设计了一段代码,利用一个函数结合本节最开头的枚举类Day来进行表示:

fun schedule(day: Day, sunny: Boolean) = {

    if (day == Day.SAT) {

        basketball()

    } else if (day == Day.SUN) {

        fishing()

    } else if (day == Day.FRI) {

        appointment()

    } else {

        if (sunny) {

            library()

        } else {

            study()

        }

    }

}

因为存在不少if-else分支,代码显得不够优雅。对Kotlin日渐熟悉的Shaw开始意识到,更好的改进方法就是用when表达式来优化。现在我们来看看修改后的版本:

fun schedule(sunny: Boolean, day: Day) = when (day) {

    Day.SAT -> basketball()

    Day.SUN -> fishing()

    Day.FRI -> appointment()

    else -> when {

        sunny -> library()

        else -> study()

    }

}

整个函数一下子“瘦身”了很多,由于少了很多语法关键字干扰,代码的可读性也更上了一层楼。

3. when表达式具体语法

我们根据上述这段代码来分析下when表达式的具体语法:

1)一个完整的when表达式类似switch语句,由when关键字开始,用花括号包含多个逻辑分支,每个分支由->连接,不再需要switch的break(这真是一个恼人的关键字),由上到下匹配,一直匹配完为止,否则执行else分支的逻辑,类似switch的default;

2)每个逻辑分支具有返回值,最终整个when表达式的返回类型就是所有分支相同的返回类型,或公共的父类型。在上面的例子中,假设所有活动函数的返回值为Unit,那么编译器就会自动推导出when表达式的类型,即Unit。以下是一个非Unit的例子:

fun foo(a: Int) = when (a) {

    1 -> 1

    2 -> 2

    else -> 0

}

>>> foo(1)

1

3)when关键字的参数可以省略,如上述的子when表达式可改成:

when {

    sunny -> library()

    else -> study()

}

该情况下,分支->的左侧部分需返回布尔值,否则编译会报错,如:

>>> when { 1 -> 1 }

error: condition must be of type kotlin.Boolean, but is of type kotlin.Int

4)表达式可以组合,所以这是一个典型的when表达式组合的例子。你在Java中很少见过这么长的表达式,但是这在Kotlin中很常见。如果你足够仔细,还会看出这还是一个我们之前提到过的表达式函数体。

可能你会说,这样嵌套子when表达式,层次依旧比较深。要知道when表达式是很灵活的,我们很容易通过如下修改来解决这个问题:

fun schedule(sunny: Boolean, day: Day) = when {

    day == Day.SAT -> basketball()

    day == Day.SUN -> fishing()

    day == Day.FRI -> appointment()

    sunny -> library()

    else -> study()

}

是不是很优雅?其实when表达式的威力远不止于此。关于它更多的语法细节,我们会在第4章进一步介绍。同时你也将了解到如何利用when表达式结合代数数据类型,来对业务进行更好的抽象。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。