原文: https://beginnersbook.com/2019/03/kotlin-sealed-class/

密封类用于表示受限类层次结构,其中对象或值可以具有来自有限值集合的类型之一。您可以将密封类视为枚举类的扩展。枚举类中的值集也受到限制,但枚举常量只能有单个实例,而密封类的子类可以有多个实例。

为什么我们需要密封类?

让我们首先了解密封类的必要性。在下面的例子中,我们有一个类Color,它有三个子类RedOrangeBlue
这里的eval()方法中的when表达式必须使用else分支,否则我们会得到一个编译错误。现在,如果我们尝试向类Color添加子类,则else分支中的代码将执行,这是默认代码。编译器应警告我们新添加的子类的代码不存在,因此我们应该在添加新子类,而不在表达式中添加逻辑时收到警告或错误。使用Sealed类解决了这个问题。

  1. open class Color{
  2. class Red : Color()
  3. class Orange : Color()
  4. class Blue : Color()
  5. }
  6. fun eval(c: Color) =
  7. when (c) {
  8. is Color.Red -> println("Paint in Red Color")
  9. is Color.Orange -> println("Paint in Orange Color")
  10. is Color.Blue -> println("Paint in Blue Color")
  11. else -> println("Paint in any Color")
  12. }
  13. fun main(args: Array<String>) {
  14. val r = Color.Red()
  15. eval(r)
  16. }

Kotlin 密封类示例

在 Kotlin 中,在类标题中的class关键字之前使用sealed关键字声明了密封类。

让我们使用密封类在上面看到的相同示例。这里我们通过将类Color标记为密封来解决上述问题。

名称密封的类仅表示从一组有限的值中获取值。这里else分支在when表达式中不是必需的,因为编译器知道该类是密封的,它只需要三个派生类的表达式,不需要默认的else分支。现在,如果我们尝试将新的子类添加到类Color中,它将不会创建不必要的错误,而是会引发错误。

例如,如果我们在密封类Color中添加一个新的子类White,并且在表达式时不添加White子类的表达式,那么我们将得到此错误 - Error:(12, 9) Kotlin: 'when' expression must be exhaustive, add necessary 'is White' branch or 'else' branch instead

  1. sealed class Color{
  2. class Red : Color()
  3. class Orange : Color()
  4. class Blue : Color()
  5. }
  6. fun eval(c: Color) =
  7. when (c) {
  8. is Color.Red -> println("Paint in Red Color")
  9. is Color.Orange -> println("Paint in Orange Color")
  10. is Color.Blue -> println("Paint in Blue Color")
  11. }
  12. fun main(args: Array<String>) {
  13. val r = Color.Red()
  14. eval(r)
  15. }

输出:

Kotlin 密封类 - 图1

现在你可以理解,通过使类密封,我们将限制添加到类层次结构中。

Kotlin 的密封类规则

  1. 我们无法创建密封类的对象,这意味着密封类无法实例化。
  2. 密封类的所有子类必须在声明密封类的同一文件中声明。
  3. 密封类的构造函数默认为private,我们不能将其设为非私有。