原文: https://www.programiz.com/kotlin-programming/sealed-class

在本文中,您将在示例的帮助下了解密封类,如何创建它们以及何时使用它们。

当值只能具有有限集合(受限制的层次结构)中的一种类型时,使用密封类。


在详细介绍密封类之前,让我们探讨它们解决的问题。 让我们举个例子(摘自 Kotlin 官方网站-密封类文章):

  1. class Expr
  2. class Const(val value: Int) : Expr
  3. class Sum(val left: Expr, val right: Expr) : Expr
  4. fun eval(e: Expr): Int =
  5. when (e) {
  6. is Const -> e.value
  7. is Sum -> eval(e.right) + eval(e.left)
  8. else ->
  9. throw IllegalArgumentException("Unknown expression")
  10. }

在上述程序中,基类Expr具有两个派生类Const(代表一个数字)和sum(代表两个表达式的总和)。 在这里,必须使用else分支作为when表达式的默认条件

现在,如果您从Expr类派生新的子类,则编译器将不会检测到任何东西,因为else分支对其进行处理会导致错误。 如果在添加新的子类时编译器发出错误,那就更好了。

要解决此问题,可以使用密封类。 如前所述,密封类限制了创建子类的可能性。 并且,当您在when表达式中处理密封类的所有子类时,不必使用else分支。


要创建密封类,请使用密封修饰符。 例如,

  1. sealed class Expr

示例:密封类

使用密封类可以解决上述问题:

  1. sealed class Expr
  2. class Const(val value: Int) : Expr()
  3. class Sum(val left: Expr, val right: Expr) : Expr()
  4. object NotANumber : Expr()
  5. fun eval(e: Expr): Int =
  6. when (e) {
  7. is Const -> e.value
  8. is Sum -> eval(e.right) + eval(e.left)
  9. NotANumber -> java.lang.Double.NaN
  10. }

如您所见,没有else分支。 如果您从Expr类派生新的子类,则除非该子类在when表达式中进行处理,否则编译器将抱怨。


几个重要说明

  • 密封类的所有子类必须在声明了密封类的同一文件中声明。
  • 密封类本身就是抽象,您不能从中实例化对象。
  • 您不能创建密封类的非私有构造器。 它们的构造器默认为private

枚举与密封类之间的区别

枚举类和密封类非常相似。 枚举类型的值集也像密封类一样受到限制。

唯一的区别是,枚举只能有一个实例,而密封类的子类可以有多个实例。