Kotlin 密封类
提示
- 密封类的定义与用途:Kotlin 的密封类用于限制类层次结构,只允许从有限的一组类型中选择。使用
sealed
修饰符来定义密封类,这限制了派生类的类型,确保它们都在同一个文件中声明。 - 解决
when
表达式的默认条件问题:使用密封类可以避免在when
表达式中使用else
作为默认条件。如果新增了密封类的子类而未在when
表达式中处理,编译器会发出警告,从而提高代码的安全性。 - 密封类与枚举类的区别:密封类与枚举类类似,但主要区别在于密封类的子类可以有多个实例,而枚举类的每个类型只能有一个实例。密封类的构造函数默认为私有,且必须在同一个文件中声明所有子类。
密封类在值只能从有限集合中的一种类型中选择时使用(限制了层次结构)。
在深入了解密封类之前,让我们探讨它们解决的问题。我们来看一个例子(来自官方 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
。
枚举类和密封类之间的区别
枚举类 和密封类非常相似。枚举类型的值集也像密封类一样受到限制。
唯一的区别是,枚举可以只有一个实例,而密封类的子类可以有多个实例。