Kotlin sealed class 的使用

什么是 sealed class

先来看官网的定义:

  • 密封类用来表示受限的类继承结构:当一个值为有限几种的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例

密封类和枚举同作为一个值的列举功能,但是密封类的功能更加强大。体现在:

  • 密封类的每个子类可以有多个对象,枚举常量就是枚举类型的一个对象
  • 密封类子类可以持有其他引用数据,枚举常量只能持有枚举类构造方法传入的值
  • 密封类可以自定义方法,枚举常量只能重写枚举类的方法

sealed class 的声明

在 Kotlin 1.1 之前,子类必须嵌套在密封类声明的内部。从 1.1 开始,变更为直接子类可以嵌套在密封类内部,也可以在密封类外,但是必须在密封类的 $\color{red} {Red} $ 同一个文件下面,间接子类则可以放在任意一个地方。
先来看个简单的加载状态的密封类的声明

// 声明一个密封类 LoadState
sealed class LoadState {
    abstract val stateMessage: String
}

/**
 * 直接继承密封类 LoadState,可以嵌套在 LoadState 内部,也可以放在同文件下
 * 由于没有引用其他数据,这里建议直接用单例,可以复用
 */
object LoadStart: LoadState() {
    override val stateMessage: String
        get() = "Start load"
}

class LoadSuccess<out T>(val result: T): LoadState() {
    override val stateMessage: String
        get() = "load success"
}

class LoadFail(private val throwable: Throwable): LoadState() {
    override val stateMessage: String
        get() = "load failed with exception : ${Log.getStackTraceString(throwable)}"
}

如果用枚举去声明这个加载状态

enum class LoadState(val stateMessage: String) {
    LoadStart("Start load"),
    LoadSuccess("load success"),
    LoadFail("load failed")
}

想要在枚举常量中的 LoadSuccess 和 LoadFail 中单独加入加载结果 result 和 Throwable 信息是不能够的。除非是在枚举类当中再加入 result 和 Throwable 变量,而 LoadSuccess 不需要 Throwable,LoadFail 不需要
result, LoadStart 更是都不需要,这就造成变量的冗余和资源的浪费。

enum class LoadState(val stateMessage: String) {
    LoadStart("Start load") {
        // LoadStart 不需要这两个字段
        override var result: Any
            get() = TODO("Not yet implemented")
            set(value) {}
        override var throwable: Throwable
            get() = TODO("Not yet implemented")
            set(value) {}
    },
    LoadSuccess("load success") {
        override var result: Any
            get() = TODO("Not yet implemented")
            set(value) {}
        // LoadSuccess 不需要这两个字段
        override var throwable: Throwable
            get() = TODO("Not yet implemented")
            set(value) {}
    },
    LoadFail("load failed") {
        // LoadFail 不需要这两个字段
        override var result: Any
            get() = TODO("Not yet implemented")
            set(value) {}
        override var throwable: Throwable
            get() = TODO("Not yet implemented")
            set(value) {}
    };
    
    abstract var result: Any
    
    abstract var throwable: Throwable
}

when 表达式中的使用

当使用 when 表达式时,对于密封类需要覆盖所有的子类,如果没有,IDE 会提醒覆盖密封类的所有子类或者加入 else 语句,以达到逻辑的完整链路,这样在开发时就保证了程序的稳定性

fun testLoadState(loadState: LoadState) {
    val result = when (loadState) {
        LoadStart -> showLoadStartToast(loadState.stateMessage)
        is LoadSuccess<*> -> showResult(loadState.result)
        is LoadFail -> showLoadFailDialog(loadState.stateMessage)
    }
}

这一点普通类是无法做到的,因为编译器不知道这个普通类到底有几个子类,一定要在最后加上一条 else 分支。假设 LoadState 是个普通抽象类

abstract class LoadState {
    abstract val stateMessage: String
}

fun testLoadState(loadState: LoadState) {
    val result = when (loadState) {
        LoadStart -> showLoadStartToast(loadState.stateMessage)
        is LoadSuccess<*> -> showResult(loadState.result)
        is LoadFail -> showLoadFailDialog(loadState.stateMessage)
        else -> {} // 不加入这条 else 分支,编译器会提示错误
    }
}

总结

  • 如果当只需要一个常量集以区分不同类型,可以直接使用 enum class
  • 如果当需要一个 event,message 或者 state 类型,需要携带其他数据时,推荐使用 sealed class

参考文档

https://www.kotlincn.net/docs/reference/sealed-classes.html

https://blog.kotlin-academy.com/enum-vs-sealed-class-which-one-to-choose-dc92ce7a4df5

https://carterchen247.medium.com/kotlin%E4%BD%BF%E7%94%A8%E5%BF%83%E5%BE%97-sealed-class-82eccf890ac0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin 中,sealed class(密封类)是一种特殊的类别,用于表示受限制的类继承结构。它可以作为父类限制其子类的数量,使得编译器能够对可能的继承关系进行静态分析。 以下是 sealed class 的基本特性和用法: 1. sealed class 用关键字 `sealed` 进行声明,可以有多个子类。 2. sealed class 是抽象的,不能直接实例化。 3. sealed class 的子类必须在同一个文件中定义,不能在其他文件中定义。 4. 子类可以是数据类、普通类或者对象声明。 5. sealed class 的子类之间可以相互继承,但只能在同一个文件中进行。 使用 sealed class 的一个常见场景是用于表示有限的状态或类型。通过限制子类的数量,可以确保所有可能的情况都在编译时被考虑到,从而避免了在运行时出现意外的情况。 以下是一个使用 sealed class 表示表达式的例子: ```kotlin sealed class Expression { class Constant(val value: Int) : Expression() class Addition(val left: Expression, val right: Expression) : Expression() class Subtraction(val left: Expression, val right: Expression) : Expression() } ``` 在这个例子中,`Expression` 是一个 sealed class。它有三个子类:`Constant`、`Addition` 和 `Subtraction`。这个 sealed class 用于表示一个数学表达式,它可以是一个常量、一个加法表达式或一个减法表达式。 使用 sealed class 可以有效地限制可能的表达式类型,并且在处理表达式时,编译器能够静态检查确保所有情况都被考虑到。 总之,sealed classKotlin 中用于创建受限制的类继承结构的一种机制,适用于表示有限状态或类型的情况。它提供了一种安全、可控的方式来定义和处理子类,并且允许编译器在编译时进行静态分析和检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值