11月16日 JetBrains 正式发布了 Kotlin 1.6.0,这个版本根据一些反馈,将上一版本中的一些实验的语法特性进行了转正。例如

  • Sealed exhaustive whens
  • Suspending functions as supertypes
  • ...

Sealed exhaustive whens

当我们在 when 中使用诸如枚举、密封类/接口等可穷举类型时,某些情况下可能写出不安全的代码

sealed class Contact {
   data class PhoneCall(val number: String) : Contact()
   data class TextMessage(val number: String) : Contact()
   data class InstantMessage(val type: IMType, val user: String) : Contact()
}

如上,定义了三个子类的密封类

fun Rates.computeMessageCost(contact: Contact): Cost =
   when (contact) { // ERROR: 'when' expression must be exhaustive
       is Contact.PhoneCall -> phoneCallCost
       is Contact.TextMessage -> textMessageCost
   }

此时如果 case 后跟的是一个表达式, 则如果 case 分支没有穷举所有子类,编译器会报错

但是如果如果 case 后个的是一个语句,如下

fun sendAnnouncement(contact: Contact, announcement: Announcement) {
   when (contact) {
       is Contact.PhoneCall -> schedulePhoneCall(contact.number, announcement)
       is Contact.TextMessage -> sendTextMessage(contact.number, announcement)
   }
}

此时即使没有穷举所有子类,编译器也不会报错,这可能会造成不必要的bug

Kotlin 1.6 在这种情况下,编译器会给出 Warning ,按计划 1.7 之后 Warning 会改为 Error,强制开发者补齐所有分支逻辑,避免出现 Bug

Suspending functions as supertypes

Kotlin 中许多 API 都以函数类型作为参数。当你需要调用这些 API 时,需要传入一个函数类型的实例。而当你想在实例中封装一些可复用的逻辑时,可以使用函数类型作为父类创建子类。

但是这种做法目前不适用于挂起函数,你无法继承一个 suspend 函数类型的父类

class C : suspend () -> Unit { // Error: Suspend function type is not allowed as supertypes 
}


C().startCoroutine(completion = object : Continuation<Unit> {
    override val context: CoroutineContext
        get() = TODO("Not yet implemented")

    override fun resumeWith(result: Result<Unit>) {
        TODO("Not yet implemented")
    }
})

Kotlin 1.5.30 在 Preveiw 中引入了此特性,可以继承一个 suspend 的函数类型

class MyClickAction : suspend () -> Unit {
    override suspend fun invoke() { TODO() }
}

fun launchOnClick(action: suspend () -> Unit) {}

如上,你现在可以这样调用 launchOnClick(MyClickAction())

1.6 中将此 feature 默认打开。

此外 1.6 还加入了其他一些新的语法特性,详情可以参考:blog.jetbrains.com/kotlin/2021…