使用密封类强制编译错误

nha*_*man 37 kotlin

使用密封类,您可以使用详尽的when表达式,并else在表达式返回结果时省略该子句:

sealed class SealedClass {
  class First : SealedClass()
  class Second : SealedClass()
}

fun test(sealedClass: SealedClass) : String =
    when (sealedClass) {
      is SealedClass.First -> "First"
      is SealedClass.Second -> "Second"
    }
Run Code Online (Sandbox Code Playgroud)

现在,如果我是一个补充ThirdSealedClass,编译器会抱怨说,when在表达test()并不详尽,我需要添加一个子句Thirdelse.

我想知道,如果test()不返回任何内容,是否也可以强制执行此检查:

fun test(sealedClass: SealedClass) {
    when (sealedClass) {
      is SealedClass.First -> doSomething()
      is SealedClass.Second -> doSomethingElse()
    }
}
Run Code Online (Sandbox Code Playgroud)

如果Third添加,此代码段不会中断.我return之前可以添加一个语句when,但这很容易被遗忘,如果其中一个子句的返回类型不是,则可能会中断Unit.

我如何确保不忘记在我的when条款中添加分支?

vod*_*dan 23

强制执行详尽无遗的方法when是使用其值使其成为表达式:

sealed class SealedClass {
    class First : SealedClass()
    class Second : SealedClass()
    class Third : SealedClass()
}

fun test(sealedClass: SealedClass) {
    val x = when (sealedClass) {
        is SealedClass.First -> doSomething()
        is SealedClass.Second -> doSomethingElse()
    }  // ERROR here

    // or

    when (sealedClass) {
        is SealedClass.First -> doSomething()
        is SealedClass.Second -> doSomethingElse()
    }.let {}  // ERROR here
}
Run Code Online (Sandbox Code Playgroud)


nha*_*man 23

在Voddan的回答中,你可以建立一个safe你可以使用的属性:

val Any?.safe get() = Unit
Run Code Online (Sandbox Code Playgroud)

使用:

when (sealedClass) {
    is SealedClass.First -> doSomething()
    is SealedClass.Second -> doSomethingElse()
}.safe
Run Code Online (Sandbox Code Playgroud)

我认为它提供了一个更清晰的信息,而不仅仅是将.let{}结果附加或赋值给一个值.


Kotlin追踪器上存在一个未解决的问题,即考虑支持"密封晃动".

  • 这样做的缺点是它会污染自动完成,因为此扩展方法将显示在每个语句上. (3认同)
  • @Tunga取代任何?单位?它不会污染太多 (3认同)

Seb*_*igo 18

我们的方法避免在自动完成时随处可用.使用此解决方案,您还可以在编译时使用when返回类型,以便继续使用when返回类型的函数.

Do exhaustive when (sealedClass) {
  is SealedClass.First -> doSomething()
  is SealedClass.Second -> doSomethingElse()
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样定义此对象:

object Do {
    inline infix fun<reified T> exhaustive(any: T?) = any
}
Run Code Online (Sandbox Code Playgroud)

  • 有趣的做法! (4认同)

Max*_*Max 10

我们可以在类型 T 上创建一个扩展属性,其名称有助于解释目的

val <T> T.exhaustive: T
    get() = this
Run Code Online (Sandbox Code Playgroud)

然后在任何地方使用它

when (sealedClass) {
        is SealedClass.First -> doSomething()
        is SealedClass.Second -> doSomethingElse()
    }.exhaustive
Run Code Online (Sandbox Code Playgroud)

它是可读的,准确地显示它的作用,如果没有涵盖所有情况,将显示错误。 在这里阅读更多