7

Kotlin 1.4.30-RC 密封接口来啦!

 3 years ago
source link: https://www.bennyhuo.com/2021/01/23/kotlin-sealed-interface/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

密封类是 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 行不行?

Ev2EbyU.png!mobile

三选一,

第一种: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 类型的时候也多了个选择:

JjmmuyQ.png!mobile

而且你会神奇的发现,内联类跟密封接口可以一起使用了:

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 语言系统精讲》 即可进入课程

iAZzM3R.png!mobile

Kotlin 协程对大多数初学者来讲都是一个噩梦,即便是有经验的开发者,对于协程的理解也仍然是懵懵懂懂。如果大家有同样的问题,不妨阅读一下我的新书《深入理解 Kotlin 协程》,彻底搞懂 Kotlin 协程最难的知识点:

扫描二维码或者点击链接 《深入理解 Kotlin 协程》 购买本书

Ufim2eb.png!mobile

如果大家想要快速上手 Kotlin 或者想要全面深入地学习 Kotlin 的相关知识,可以关注我基于 Kotlin 1.3.50 全新制作的入门课程:

扫描二维码或者点击链接 《Kotlin 入门到精通》 即可进入课程

UZRR7f.png!mobile

Android 工程师也可以关注下《破解Android高级面试》,这门课涉及内容均非浅尝辄止,除知识点讲解外更注重培养高级工程师意识,目前已经有 1100 多位同学在学习:

扫描二维码或者点击链接 《破解Android高级面试》 即可进入课程

mUvYjuZ.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK