密封类用于当一个值只能是有限集合中的一个类型时(受限的层次结构)。
在详细介绍密封类之前,让我们先了解一下它们解决了什么问题。我们来看一个例子(摘自 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(表示两个表达式的和)。在这里,在when 表达式中使用 else
分支作为默认条件是强制性的。
现在,如果您从 Expr
类派生出一个新的子类,编译器将不会检测到任何内容,因为 else
分支会处理它,这可能导致错误。如果我们添加一个新的子类,编译器能发出错误该多好。
为了解决这个问题,您可以使用密封类。如前所述,密封类限制了创建子类的可能性。而且,当您在 when
表达式中处理密封类的所有子类时,就不需要使用 else
分支了。
要创建密封类,需要使用 sealed 修饰符。例如:
sealed class Expr
示例:密封类
您可以使用密封类来解决上述问题:
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
的。
枚举类和密封类之间的区别
枚举类和密封类非常相似。枚举类型的取值集合也像密封类一样受到限制。
唯一的区别是,枚举只能有一个实例,而密封类的子类可以有多个实例。