Kotlin学习笔记(十一)Sealed类和Sealed接口

最近在看DataStore源码时,看到一种类似于枚举的使用方式。就是Sealed类,但是比枚举类更有扩展性,除了拥有共享属性,还可以持有特征属性。

1、通常限定一些有限集合的状态值.
      如: 网络状态:成功,失败.
            账户状态:vip,普通.
2、与when配合使用,无需if-else

先讲两个使用案例:

第一个案例:


/**
 * Represents the current state of the DataStore.
 */
private sealed class State<T>

private object UnInitialized : State<Any>()

/**
 * A read from disk has succeeded, value represents the current on disk state.
 */
private class Data<T>(val value: T, val hashCode: Int) : State<T>() {
    fun checkHashCode() {
        check(value.hashCode() == hashCode) {
            "Data in DataStore was mutated but DataStore is only compatible with Immutable types."
        }
    }
}

/**
 * A read from disk has failed. ReadException is the exception that was thrown.
 */
private class ReadException<T>(val readException: Throwable) : State<T>()

/**
 * The scope has been cancelled. This DataStore cannot process any new reads or writes.
 */
private class Final<T>(val finalException: Throwable) : State<T>()

上述代码中,UnInitialized,Data,ReadException,Final都是State的四种不同状态。相当于,枚举类State,有这四个状态成员。

State状态类,虽然没有共享属性,但是可以有;

UnInitialized状态类,虽然没有特征属性,但是也可以有;

Data状态类,有特征属性:value, hashcode;

ReadException状态类,有特征属性:readException;

Final状态类,有特征属性finalException。

当然也可以有属于自己的方法,Data状态类有就:fun checkHashCode().

使用时可以这样使用:

密封类通常与表达式一起使用。

在判断出状态类型后,根据自带的特有属性顺便在状态里把事情做了。

                when (it) {
                    is ReadException<T> -> throw it.readException
                    is Final<T> -> throw it.finalException
                    is Data<T> -> it.value
                    is UnInitialized -> error(
                        "This is a bug in DataStore. Please file a bug at: " +
                            "https://issuetracker.google.com/issues/new?" +
                            "component=907884&template=1466542"
                    )
                }

有没有发现?不需要else处理default,因为编译器知道Sealed类的所有子状态类,就没必要了。这是区别于普通类的情况。

第二个案例:

这个案例是想说, 通过扩展函数还可以做进一步的判断,执行更多的业务。

sealed class Result<out R> {

    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val throwable: Throwable?) : Result<Nothing>()
    object Loading : Result<Nothing>()

    override fun toString(): String {
        return when (this) {
            is Success<*> -> "Success[data=$data]"
            is Error -> "Error[throwable=$throwable]"
            Loading -> "Loading"
        }
    }
}

val Result<*>.succeeded
    get() = this is Success && data != null

fun <T> Result<T>.successOr(fallback: T): T {
    return (this as? Success<T>)?.data ?: fallback
}

val <T> Result<T>.data: T?
    get() = (this as? Success)?.data

inline fun <reified T> Result<T>.updateOnSuccess(stateFlow: MutableStateFlow<T>) {
    if (this is Success) {
        stateFlow.value = data
    }
}

总结一下Sealed类:

  • 密封类以及它的子类最好都定义在同一个文件中,这样会更加内聚。但不是必须的! 最新的kotlin版本只要求同Module、同包名
  • 密封类隐式是一个无法实例化的抽象类。
  • 它用于表示受限制的类层次结构。
  • 当对象具有来自有限集的类型之一,但不能具有任何其他类型时。这是使用密封类。类似于枚举使用的地方。
  • 密封类的构造函数在默认情况下是私有的,它也不能允许声明为非私有。
  • 对比普通类,Sealed类自动生成toString、hashCode、equals函数;
  • 结合扩展函数做更进一步的事情。

从1.5开始,有增加了Sealed接口。没有任何成员定义的Sealed类,可以用Sealed接口替代。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值