转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120496247
本文出自【赵彦军的博客】
文章目录
- 简介
- 密封类与枚举类对比
- 创建状态集
- 使用
- 进一步简化
- 举例
简介 密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。
在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
声明一个密封类,使用
sealed
修饰类,密封类可以有子类。sealed
不能修饰 interface
,`abstract classe(会报 warning,但是不会出现编译错误)密封类与枚举类对比 ① 相同点 ( 类型限制 ) : 从类型种类角度对比 , 类与枚举类类似 , 枚举类的值的集合是受限制的 , 不能随意扩展 ;
② 不同点 ( 对象个数限制 ) : 从每个类型对象个数对比 , 枚举类的每个类型只能存在一个实例 , 而密封类的每个类型可以创建无数个实例 ;
创建状态集 密封类声明:在 class 前添加 sealed 修饰符 , 即可将该类声明为密封类 ;
和抽象类类似,Sealed Class可用于表示层级关系。它的子类可以是任意的类:data class、普通Kotlin对象、普通的类,甚至也可以是另一个密封类,所以,我们定义一个Result Sealed Class:
*/
sealed class Result {
//定义网络请求成功
data class OK(val result: String) : Result()//定义网络请求失败
data class FAIL(val throwable: Throwable) : Result()
}
当然,也不一定非要写在顶层类中:
//定义结果密封类
sealed class Result//定义网络请求成功
data class OK(val result: String) : Result()//定义网络请求失败
data class FAIL(val throwable: Throwable) : Result()
这样也是可以的,它们的区别在于引用的时候,是否包含顶层类来引用而已。
大部分场景下,还是建议第一种方式,可以比较清晰的展示调用的层级关系。
使用 接下来,我们来看下如何使用Sealed Class。
fun main() {
//模拟封装枚举的产生
val result = if (true) {
Result.OK("Success")
} else Result.FAIL(Exception("error"))when (result) {
is Result.OK -> println(result.result)is Result.FAIL -> println(result.throwable)
}
}
大部分场景下,Sealed Class都会配合when一起使用,同时,如果when的参数是Sealed Class,在IDE中可以快速补全所有分支,而且不会需要你单独补充else 分支,因为Sealed Class已经是完备的了。
所以when和Sealed Class真是天作之合。
进一步简化 其实我们还可以进一步简化代码的调用,因为我们每次使用Sealed Class的时候,都需要when一下,有些时候,也会产生一些代码冗余,所以,借助拓展函数,我们进一步对代码进行简化。
inline fun Result.doSuccess(success: (String) -> Unit) {
if (this is Result.OK) {
success(result)
}
}inline fun Result.doError(error: (Exception) -> Unit) {
if (this is Result.FAIL) {
error(throwable)
}
}
我们在使用一下:
fun main() {
//模拟封装枚举的产生
val result = if (true) {
Result.OK("Success")
} else Result.FAIL(Exception("error"))result.doSuccess {}result.doError {}
}
【Kotlin实战指南|Kotlin密封类sealed】是不是简单很多
举例 假如在 Android 中我们有一个 view,我们现在想通过 when 语句设置针对 view 进行两种操作:显示和隐藏,那么就可以这样做:
sealed class UiOp {
object Show: UiOp()
object Hide: UiOp()
}
fun execute(view: View, op: UiOp) = when (op) {
UiOp.Show -> view.visibility = View.VISIBLE
UiOp.Hide -> view.visibility = View.GONE
}
以上功能其实完全可以用枚举实现,但是如果我们现在想加两个操作:水平平移和纵向平移,并且还要携带一些数据,比如平移了多少距离,平移过程的动画类型等数据,用枚举显然就不太好办了,这时密封类的优势就可以发挥了,例如:
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 // 这个 when 语句分支不仅告诉 view 要水平移动,还告诉 view 需要移动多少距离,这是枚举等 Java 传统思想不容易实现的
is UiOp.TranslateY -> view.translationY = op.px
}
以上代码中,
TranslateX
是一个类,它可以携带多于一个的信息,比如除了告诉 view
需要水平平移之外,还可以告诉 view
平移多少像素,甚至还可以告诉 view
平移的动画类型等信息,我想这大概就是密封类出现的意义吧。除此之外,如果
when
语句的分支不需要携带除“显示或隐藏view之外的其它信息”时(即只需要表明 when
语句分支,不需要携带额外数据时),用 object
关键字创建单例就可以了,并且此时 when
子句不需要使用 is
关键字。最后,我们甚至可以把这一组操作封装成一个函数,以便日后调用,如下:
// 先封装一个UI操作列表
class Ui(val uiOps: List = emptyList()) {
operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}// 定义一组操作
val ui = Ui() +
UiOp.Show +
UiOp.TranslateX(20f) +
UiOp.TranslateY(40f) +
UiOp.Hide
// 定义调用的函数
fun run(view: View, ui: Ui) {
ui.uiOps.forEach { execute(view, it) }
}run(view, ui) // 最终调用
推荐阅读
- java|你不知道的 java 对象序列化的秘密
- android|Kotlin Sealed Class
- 笔记|weblogic 8.1 控制台密码丢失了怎么办
- 架构|了解什么是架构基本概念和架构本质
- spring|Spring Cloud微服务治理框架深度解析
- 经典面试题(Integer c=100,d=100,c==d 一定是false吗())
- 极客专栏打包-实时更新含文档源码
- #|【微服务】一文读懂网关概念+Nginx正反向代理+负载均衡+Spring Cloud Gateway(多栗子)
- java|SpringBoot新闻管理系统——shiro+SpringCloud微服务