当值只能具有有限集合(受限制的层次结构)中的一种类型时,将使用密封Sealed类。
在详细介绍密封类之前,让我们探讨它们解决的问题。让我们举个例子(摘自Kotlin官方网站-密封类文章):
class Expr
class Const(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
fun eval(e: Expr): Int =
when (e) {
is Const -> e.value
is Sum -> eval(e.right) + eval(e.left)
else ->
throw IllegalArgumentException("Unknown expression")
}
在上面的程序中,基类Expr具有两个派生类Const (代表一个数字)和Sum (代表两个表达式的总和)。在这里,必须在表达式中使用else
分支作为默认条件。
现在,如果您从Expr
类派生一个新的子类,则编译器将不会检测到任何东西,因为else
分支会对其进行处理,这可能会导致错误。如果在添加新的子类时编译器发出错误,那就更好了。
要解决此问题,可以使用密封类。如前所述,密封类限制了创建子类的可能性。而且,当您在when
表达式中处理密封类的所有子类when
,不必使用else
分支。
若要创建密封类,请使用密封修饰符。例如,
sealed class Expr
示例:密封Sealed类
这是使用密封Sealed类解决上述问题的方法:
sealed class Expr
class Const(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
object NotANumber : Expr()
fun eval(e: Expr): Int =
when (e) {
is Const -> e.value
is Sum -> eval(e.right) + eval(e.left)
NotANumber -> java.lang.Double.NaN
}
如您所见,没有else
分支。如果从Expr
类派生新的子类,则除非该子类在when
表达式中进行处理,否则编译器将抱怨。
几个重要注意事项
- 密封类的所有子类必须在声明密封类的同一文件中声明。
- 密封类本身是抽象的,您不能从中实例化对象。
- 您不能创建密封类的非私有构造函数。它们的构造函数默认是
private
的。
枚举和密封Sealed类之间的区别
枚举类和密封类非常相似。枚举类型的值集也像密封类一样受到限制。
唯一的区别是,枚举只能有一个实例,而密封类的子类可以有多个实例。