android|Kotlin Sealed Class

Sealed Classes 是什么?
我们先来看一下官方对 Sealed Classes 的解释
我们将上面这段话,简单的总结一下:
Sealed Classes 用于表示受限制的类层次结构
从某种意义上说,Sealed Classes 是枚举类的扩展
枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例
那上面这三段话分别是什么意思呢?接下来我们围绕这三个方面来分析。
Sealed Classes 用于表示受限制的类层次结构
Sealed Classes 用于表示受限制的类层次结构,其实这句话可以拆成两句话来理解。
Sealed Classes 用于表示层级关系: 子类可以是任意的类, 数据类、Kotlin 对象、普通的类,甚至也可以是另一个 Sealed
Sealed Classes 受限制: 必须在同一文件中,或者在 Sealed Classes 类的内部中使用,在Kotlin 1.1 之前,规则更加严格,子类只能在 Sealed Classes 类的内部中使用
Sealed Classes 的用法也非常的简单,我们来看一下如何使用 Sealed Classes。
sealed class Color {
class Red(val value: Int) : Color()
class Green(val value: Int) : Color()
class Blue(val name: String) : Color()
}
fun isInstance(color: Color) {
when (color) {
is Color.Red -> TODO()
is Color.Green -> TODO()
is Color.Blue -> TODO()
}
}

在这里推荐大家一个快捷键 Mac/Win/Linux:Alt + Enter 可以补全 when 语句下的所有分支,效果如下所示:
更多 AndroidStudio 快捷键,可以看之前的两篇文章
为数不多的人知道的AndroidStudio快捷键(一)
为数不多的人知道的AndroidStudio快捷键(二)
Sealed Classes 是枚举类的扩展
从某种意义上说,Sealed Classes 是枚举类的扩展,其实 Sealed Classes 和枚举很像,我们先来看一个例子。
正如你所看到的,在 Sealed Classes 内部中,使用 object 声明时,我们可以重用它们,不需要每次创建一个新实例,当这样使用时候,它看起来和枚举非常相似。
注意:实际上很少有人会这么使用,而且也不建议这么用,因为在这种情况枚举比 Sealed Classes 更适合
在什么情况下使用枚举
如果你不需要多次实例化,也不需要不提供特殊行为,或者也不需要添加额外的信息,仅作为单个实例存在,这个时候使用枚举更加合适。
Sealed Classes 的子类可以表示不同状态的实例
与枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例,我们来看个例子可能更容易理解这句话。
这里我们延用之前在 Google 推荐在项目中使用 sealed 和 RemoteMediator 这篇文章中用到的例子,在请求网络的时候需要对成功或者失败进行处理,我们来看一下用 Sealed Classes 如何进行封装。
sealed class PokemonResult {
data class Success(val value: T) : PokemonResult()
data class Failure(val throwable: Throwable?) : PokemonResult()
}

这里只贴出来部分代码,核心实现可以查看项目 PokemonGo
GitHub 地址:https://github.com/hi-dhl/PokemonGo
代码路径:PokemonGo/app/…/com/hi/dhl/pokemon/data/remote/PokemonResult.kt
【android|Kotlin Sealed Class】一起来看一下如何使用
when (result) {
is PokemonResult.Failure -> {
// 进行失败提示
}
is PokemonResult.Success -> {
// 进行成功处理
}
}

我们在来看另外一个例子,在一个列表中可能会有不同类型的数据,比如图片、文本等等,那么用 Sealed Classes 如何表示。
sealed class ListItem {
class Text(val title: String, val content: String) : ListItem()
class Image(val url: String) : ListItem()
}

这是两个比较常见的例子,当然 Sealed Classes 强大不止于此,还有更多场景,等着一起来挖掘。
我们来看一下大神 Antonio Leiva 在这篇文章 Sealed classes in Kotlin: enums with super-powers 分享的一个比较有趣的例子,对 View 进行的一系列操作可以封装在 Sealed Classes 中,我们来看一下会有什么样的效果。
sealed class UiOp {
object Show: UiOp()
object Hide: UiOp()
class TranslateX(val px: Float): UiOp()
class TranslateY(val px: Float): UiOp()
}
fun execute(view: View, op: UiOp) = when (op) {
UiOp.Show -> view.visibility = View.VISIBLE
UiOp.Hide -> view.visibility = View.GONE
is UiOp.TranslateX -> view.translationX = op.px
is UiOp.TranslateY -> view.translationY = op.px
}

在 Sealed Classes 类中,我们定义了一系列 View 的操作 Show 、 Hide 、 TranslateX 、 TranslateY ,现在我们创建一个类,将这些对视图的操作整合在一起。
class Ui(val uiOps: List = emptyList()) {
operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}

    推荐阅读