咱们好,kotlin密封类sealed class不知道咱们日常开发中有没有用到,这个在封装网络状况、UI状况等方面都是十分有协助的;此外kotlin1.5版别还提供了密封接口sealed interface进一步拓宽了密封的运用场景;并且为了便利咱们运用,kotlin高版别密封类的受限制层次结构也发生了改变。

假如上面的特性你都不是很了解,没有关系,那就赶紧往下仔细阅读文章了解一下吧。

一. 密封类sealed class

密封类咱们能够理解为一个抽象类(其实它本质上便是一个抽象类),界说一些通用的行为或许标识,然后子类承继该密封类,咱们看下下面这个例子:

sealed class NetworkState<out T> {
    data class Success<T>(val response: T) : NetworkState<T>()
    data class Fail(val error: Throwable?) : NetworkState<Nothing>()
    object NetDown : NetworkState<Nothing>()
}

PS:题外话:为什么NetworkState类泛型需要添加out声明?

这儿咱们看下Fail类,能够看到它承继的父类为NetworkState<Nothing>,在kotlin中,Nothing是任意类的子类。这儿的场景简单表述为便是Nothing能够是A类的子类型,但是却不代表NetworkState<Nothing> NetworkState<A> 的子类型

想要完成这个效果,那就得要让NetworkState泛型支持协变,在java中运用extends,在kotlin中就得运用out声明。

这儿咱们界说一个密封类NetworkState封装网络状况,以及运用子类Success、Fail、NetDown分别标识恳求成功、失利和断网的具体状况。

因为每次网络恳求成功的呼应数据、失利反常都有可能不同的,所以这儿咱们支持SuccessFail动态创立,而断网状况是不会发生改变,所以运用了单例创立NetDown

在代码中咱们就能够这样运用:

fun <T> response(state: NetworkState<T>) {
    when (state) {
        is NetworkState.Success -> {
            //呼应成功
            val data = state.response
        }
        is NetworkState.Fail -> {
            //呼应失利
            val error = state.error
        }
        is NetworkState.NetDown -> {
            //断网处理
        }
    }
}

运用起来就如上面所说的,看起来抽象类也能完成这种效果,那为啥kotlin官方要专门搞一个密封类sealed class出来呢,这儿说下本人的观点:

  1. 定制优化

对密封类进行的when遍历处理,kotlin1.5.30版别新增了一个特性:

kotlin密封sealed class/interface的迭代之旅

when表达式会对是否罗列出密封类的一切子类并进行处理进行校验,一旦发现漏处理了其中的一个或许多个,会进行告警,高版别1.7及以上会直接给你报错,这对于保证代码安全行方面仍是十分的友好的。

假如想要启动这个特性,需要在build中新增如下装备:

kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.6"
        }
    }
}

假如kotlin1.7版别以下想要将告警转化为error报错,直接在上面的装备中添加装备progressiveMode = true即可。

  1. 便利管理

这个放在下面的密封受限制层次结构的改变中进行解说。

上面的这种密封类有一个不好的当地便是单承继的问题,本质上密封类的本质便是个抽象类(下面会原理剖析),所以子类最多只能承继一个类,而下面的密封接口密封接口sealed interface就处理了这个问题:支持子类完成多个密封接口

二. 密封接口sealed interface

kotlin密封sealed class/interface的迭代之旅

密封接口的作业效果和密封类相同,最终的差异便是咱们上面提及的一个类能够完成多个密封接口, 接下来咱们实际操作下

sealed interface Language {
    val language: String
}
sealed interface Location {
    val location: String
}
sealed interface Family {
    val family: String
}
data class Woman(
    override val language: String,
    override val location: String,
    override val family: String
) : Language, Location, Family
data class Child(
    override val language: String,
    override val location: String,
    override val family: String
) : Language, Location, Family

运用起来也和上面一样:

kotlin密封sealed class/interface的迭代之旅

三. 密封受限制承继层次结构的改变

在kotlin1.5以下,界说的密封类的子类一定要和密封类在同一个文件中,不然就会报错,比如:

kotlin1.4.30版别中,在kotlin1.5这个文件中新增新增一个密封类

kotlin密封sealed class/interface的迭代之旅

在另一个文件中界说子类承继该密封类就会报错:

kotlin密封sealed class/interface的迭代之旅
kotlin密封sealed class/interface的迭代之旅

而到了kotlin1.5.0版别,因为新增了这么一个特性:

kotlin密封sealed class/interface的迭代之旅

将子类承继密封类的限制放大到了整个package下,所以同一个包下的其他文件中,界说子类承继密封类将不会产生报错

请注意,密封类的子类不能是匿名类和局部类:

kotlin密封sealed class/interface的迭代之旅
kotlin密封sealed class/interface的迭代之旅

四. 密封原理探析

这儿咱们简单了解下密封类的完成原理,反编译看下:

kotlin密封sealed class/interface的迭代之旅

如前面所说,密封类本质上便是个抽象类,下面的子类都是承继该抽象类:

kotlin密封sealed class/interface的迭代之旅
kotlin密封sealed class/interface的迭代之旅
kotlin密封sealed class/interface的迭代之旅

五. 总结

本篇文章主要是解说了密封类和密封接口的运用及原理,一起对于密封子类的受限制承继层次结构的改变做了一个说明,还扩展了一个小技巧:怎么敞开when遍历密封类是否罗列一切子类的处理场景并作告警处理的特性以及怎么将告警转化为error报错。

期望本篇文章能对你一切协助,能够点赞保藏支持下。

六. 历史文章

@JvmDefaultWithCompatibility优化小技巧,了解一下~

优化@BuilderInference注解,Kotlin高版别下了这些“棘手”!

七. 参阅链接

kotlinlang.org/docs/whatsn…


本文正在参与「金石方案」