跳到主要内容

Kotlin 密封类

提示
  1. 密封类的定义与用途:Kotlin 的密封类用于限制类层次结构,只允许从有限的一组类型中选择。使用 sealed 修饰符来定义密封类,这限制了派生类的类型,确保它们都在同一个文件中声明。
  2. 解决 when 表达式的默认条件问题:使用密封类可以避免在 when 表达式中使用 else 作为默认条件。如果新增了密封类的子类而未在 when 表达式中处理,编译器会发出警告,从而提高代码的安全性。
  3. 密封类与枚举类的区别:密封类与枚举类类似,但主要区别在于密封类的子类可以有多个实例,而枚举类的每个类型只能有一个实例。密封类的构造函数默认为私有,且必须在同一个文件中声明所有子类。

密封类在值只能从有限集合中的一种类型中选择时使用(限制了层次结构)。

在深入了解密封类之前,让我们探讨它们解决的问题。我们来看一个例子(来自官方 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 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

枚举类和密封类之间的区别

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

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