Open的使用
open
就像给 Kotlin 类 / 方法 "拆了锁"------ 默认情况下它们都是 "上锁" 的(不能被继承 / 重写),加了 open
才允许子类 "进门修改"。
打个生活比方:你买的新手机(普通类)默认系统是 "只读" 的,不能随便刷自定义系统(继承重写);而 open
就像给手机解了 BL 锁,现在你才能 root、刷第三方系统(子类继承并修改父类功能)。
具体用法:3 个核心场景
- 修饰类 :允许这个类被其他类继承。例:
open class Phone()
→ 子类class XiaomiPhone : Phone()
(能继承);如果不加open
,写class Phone()
,子类继承会直接报错。 - 修饰方法 :允许子类重写(覆盖)这个方法的逻辑。例:父类
open fun call() = "基础通话"
,子类override fun call() = "视频通话"
(能重写);不加open
的方法,子类不能写override
。 - 修饰属性 :允许子类重写属性的值或 getter/setter。例:父类
open val price = 1000
,子类override val price = 2000
(能重写)。
简单说,Kotlin 用 open
明确 "哪些东西可以被改",避免像 Java 那样默认全开放、容易不小心改乱父类逻辑,是一种 "安全的开放开关"。
sealed(密封类)的使用
一、sealed(密封类)怎么用?一句话总结:
限定子类范围,让编译器知道 "所有可能的情况",避免漏写判断 ,最常用在 when
表达式里。
具体用法(3 步):
- 用 sealed 修饰父类 :父类可以是 class 或 interface(Kotlin 1.5 + 支持接口),通常用
sealed class
。 - 子类必须和父类在同一文件 / 同一密封类内部:不能在其他文件定义子类,彻底锁死继承范围。
- when 表达式不用写 else:因为编译器能枚举所有子类,不用担心漏情况。
例子(比普通类更安全):
kotlin
// 1. 密封类(父类)
sealed class Result
// 2. 子类(必须在同一文件)
class Success(val data: String) : Result()
class Error(val msg: String) : Result()
// 3. when 使用(不用写 else,漏了会报错)
fun handleResult(result: Result) = when (result) {
is Success -> println("成功:${result.data}") // 编译器知道有这个子类
is Error -> println("失败:${result.msg}") // 编译器知道有这个子类
// 这里不用写 else!漏写任何子类会直接编译报错
}
二、底层原理:本质是 "编译期的语法糖"
底层没有特殊的 "密封类字节码",而是编译器帮我们做了两件事,实现 "子类范围锁定":
- 父类构造器私有化 :普通类默认有公开构造器,而 sealed 类的构造器会被编译器偷偷改成
private
,阻止外部直接 new 父类,也阻止外部文件继承(因为子类必须调用父类构造器,private 构造器只能在同一文件访问)。 - 给子类加标记 + 帮 when 做检查 :编译器会给每个子类加隐藏标记,在
when
表达式里,会自动校验 "是否覆盖了所有带标记的子类",漏了就报错 ------ 本质是编译期的静态检查,不是运行时的限制。
三、为什么要用?对比普通类的优势
比如用普通类写上面的 Result:
kotlin
// 普通父类
open class Result
class Success(val data: String) : Result()
class Error(val msg: String) : Result()
// when 必须写 else,否则编译器担心漏情况
fun handleResult(result: Result) = when (result) {
is Success -> println("成功")
is Error -> println("失败")
else -> println("未知情况") // 必须写,哪怕永远走不到
}
如果后续加了个 Loading
子类,用普通类时 when
不会报错,可能漏掉处理;但用 sealed 类,编译器会强制你在 when
里加 is Loading
的判断,避免逻辑漏洞。
Kotlin中open和sealed的使用场景
简单说:需要灵活扩展时用 open,需要固定范围时用 sealed,两者是"开放"和"封闭"的两种极端场景选择。
1. 用 open 的场景:需要"后续能加新功能"
当你不确定未来会有多少子类,想留足扩展空间时用 open
,核心是"允许新增"。
比如:
- 定义"基础组件":如
open class Animal
,后续能随时加Dog
、Cat
、Bird
等子类,不用改父类。 - 实现"多态逻辑":如
open fun pay()
(基础支付),子类能重写成WechatPay
、Alipay
,各自实现不同支付逻辑。 - 框架/库设计:比如 Android 里的
open class Activity
,允许开发者继承它写自己的MainActivity
,灵活定制页面。
2. 用 sealed 的场景:需要"明确所有可能情况"
当你能确定"子类就这么几个,不会再多了",且要避免漏处理时用 sealed
,核心是"范围锁死"。
比如:
- 定义"状态/结果":如网络请求的
sealed class Result
,只可能是Success
、Error
、Loading
,不会有其他情况,when
判断时能确保覆盖所有状态。 - 处理"固定选项":如订单状态
sealed class OrderStatus
,只有Pending
(待支付)、Paid
(已支付)、Cancelled
(已取消),后续不会新增,逻辑不会有漏洞。 - 枚举的"增强版":比普通
enum
更灵活(能传参数,如Success(val data: String)
),同时保持"选项固定"的特性。