Kotlin 1.4.30-RC 密封接口来啦!
密封类是 Kotlin 的老成员了,现在也可以有密封接口了。
前两天看到 Kotlin 1.4.30-RC 的邮件,主要添加了对 Java 15 的支持,也支持了密封接口。要知道,Java 15 当中就有个重要的特性叫密封接口,这会难道是 Kotlin 被 Java 倒逼着出了个新特性?
Java 的密封接口
我们先来看看 Java 的密封接口是怎么回事吧:
sealed interface PlayerState permits Idle, Playing, Error { } final class Idle implements PlayerState { } final class Playing implements PlayerState { } final class Error implements PlayerState { }
功能上,与 Kotlin 的密封类类似,都是限制子类个数的,所以这一点儿不应当有什么理解上的困难。
语法上,Java 秉持着它一贯的“啰嗦”的特点,在密封接口定义时,还要明确写出 permits
,告诉大家这个接口只能够被以下几个类实现。你会不会感觉很奇怪,看一下后面这几行不就知道了,为什么还有加一个 permits?因为我们编写 Java 代码的时候,通常一个类就是一个文件,因此 Java 的密封接口不会去限制只能在文件内部定义实现类(就像 Kotlin 那样),因此 permits 是必须的。
我们还注意到,PlayerState 的子类前面都加了个 final 关键字,意思就是不能被继承。这一点与 Kotlin 的密封类语法类似,Kotlin 当中类型默认就是 final 的,大家可能都没有注意过这个限制。
密封类型子类的子类
那么灵魂拷问来了,不加 final 行不行?
三选一,
第一种:sealed,就是你自己也称为密封类,这样子类还是受限制的
第二种: non-sealed,就是明确告诉大家,你不是密封类,而且不是 final,这意味着 Playing 这个类型是可以被其他类型继承的。
啊??那这样子类不就不受限制了吗?
对呀,子类是不受限制了,但直接子类的个数还是有限的。也就是说密封类实际上限制的是直接子类的个数,这一点之前我们很少提到。
第三种,final,这就比较好理解了,直接把子类的路堵死完事儿。
这么看来,Java 除了支持密封接口以外,也是直接密封类的,而且还能允许密封接口或者密封类的 non-sealed 子类有其他子类,看上去是不是比 Kotlin 高级?
非也非也!
Kotlin 的密封类的子类,也可以有子类的!列位请看:
class Song class Options sealed class PlayerState { class Error(val t: Throwable): PlayerState() object Idle: PlayerState() open class Playing(val song: Song): PlayerState() class PlayingWithOptions(song: Song, val options: Options): Playing(song) }
Playing 居然可以有个子类,叫做 PlayingWithOptions!这样搞,是不是密封类的特性就被破坏了呀?
当然不是,密封类的子类除了 Error、Idle 以外,仍然只有一种可能,那就是 Playing。这很好理解,对吧。
Kotlin 的密封接口
好了,接下来我们终于要抬出 1.4.30-RC 当中新增的 Kotlin 的密封接口了,前面的 PlayerState 里面什么都没有,显然我们把它定义成接口更好:
sealed interface PlayerState { class Error(val t: Throwable): PlayerState object Idle: PlayerState open class Playing(val song: Song): PlayerState class PlayingWithOptions(song: Song, val options: Options): Playing(song) }
为了配合密封接口的新特性,IDE 在创建 Kotlin 类型的时候也多了个选择:
而且你会神奇的发现,内联类跟密封接口可以一起使用了:
sealed interface PlayerState { // 注意这里! inline class Error(val t: Throwable): PlayerState ... }
我们在上一篇文章里面刚刚说到这事儿,虽然可以这么写,这样做意义并不大。因为密封类的子类在使用的过程中总是会声明成父类,这个过程总是会出现装箱:
val playerState: PlayerState = Idle ... playerState = Error(...) // 装箱
所以,我们几乎可以认为,内联类在密封类当中使用基本上都是错误的用法。
稍微提一句,官方在 KT-42434 Release inline classes as Stable, secure Valhalla compatibility 当中明确了 inline class 将在 1.4.30 进入 Beta 阶段,在 1.5.0 进入稳定状态;不仅如此,为了配合 Valhalla 的 Value Type 特性,后续内联类计划被改名叫做 value class,这当然都是后面的事儿了,我们后面有机会再聊。
C 语言是所有程序员应当认真掌握的基础语言,不管你是 Java 还是 Python 开发者,欢迎大家关注我的新课 《C 语言系统精讲》,上线一个月已经有 400 位同学在一起学习了:
扫描二维码或者点击链接 《C 语言系统精讲》 即可进入课程
Kotlin 协程对大多数初学者来讲都是一个噩梦,即便是有经验的开发者,对于协程的理解也仍然是懵懵懂懂。如果大家有同样的问题,不妨阅读一下我的新书《深入理解 Kotlin 协程》,彻底搞懂 Kotlin 协程最难的知识点:
扫描二维码或者点击链接 《深入理解 Kotlin 协程》 购买本书
如果大家想要快速上手 Kotlin 或者想要全面深入地学习 Kotlin 的相关知识,可以关注我基于 Kotlin 1.3.50 全新制作的入门课程:
扫描二维码或者点击链接 《Kotlin 入门到精通》 即可进入课程
Android 工程师也可以关注下《破解Android高级面试》,这门课涉及内容均非浅尝辄止,除知识点讲解外更注重培养高级工程师意识,目前已经有 1100 多位同学在学习:
扫描二维码或者点击链接 《破解Android高级面试》 即可进入课程