Java 看 Kotlin 实现了密封类,马上给自己搞了密封类和密封接口,Kotlin 一看也立马支持了密封接口。
- Java 17 更新(0):前言
- Java 17 更新(1):更快的 LTS 节奏
- Java 17 更新(2):没什么存在感的 strictfp 这回算是回光返照了
- Java 17 更新(3):随机数生成器来了一波稳稳的增强
- Java 17 更新(4):这波更新,居然利好 mac 用户
- Java 17 更新(5):历史包袱有点儿大,JDK 也在删代码啦
- Java 17 更新(6):制裁!我自己私有的 API 你们怎么随便一个人都想用?
- Java 17 更新(7):模式匹配要支持 switch 啦
- Java 17 更新(8):密封类终于转正
- Java 17 更新(9):Unsafe 不 safe,我们来一套 safe 的 API 访问堆外内存
- Java 17 更新(10):访问外部函数的新 API,JNI 要凉了?
- Java 17 更新(11):支持矢量运算,利好科学计算?
- Java 17 更新(12):支持上下文的序列化过滤器,又一次给序列化打补丁
我们书接上回,继续聊 Java 17 的更新。这篇我们介绍一下 JEP 409: Sealed Classes。
密封类从 Java 15 开始预览,Java 16 又预览了一波,终于在 Java 17 转正了(实际上 Java 16 和 17 的密封类是一样的)。
Kotlin 从 1.0 开始就有密封类,并且对子类定义位置的限制从父类内部(Kotlin 1.0)到同一个文件(Kotlin 1.1)再到同一个包内(Kotlin 1.5),但实际使用上没有什么特别大的变化 —— 直到 Java 也支持密封类和密封接口,Kotlin 才也对密封接口做了支持。
从定义上来讲,二者的密封类、接口都是限制直接子类的定义,使得直接子类是可数的。例如:
1 | package com.example.geometry; |
注意,在 Java 当中,密封类的子类的定义也有一些限制,如果父类在具名模块当中,那么子类必须也定义该模块内部;否则,子类就必须定义在父类相同的包当中。如果子类直接定义在父类当中的话,permits 就不用显式写出了:
1 | abstract sealed class Root { ... |
对于密封类的子类来讲,既可以声明为 final 来禁止被继承;也可以声明为 sealed 来使得该子类的直接子类可数;也可以声明为 non-sealed 来使得该子类的子类不受限制。因此我们说密封类可以确保其直接子类可数。例如:
1 | abstract sealed class Root { |
有了密封类再配合前面提到的 switch 模式匹配,就很好用了:
1 | Root r = new Root.A(); |
对密封接口的支持也是类似的。
密封类实际上也是一个很有用的特性,我之前在介绍 Kotlin 的密封类的时候也已经提到过不少它的用法,例如实现递归列表:
1 | public sealed interface List<T> { |
这样 List 就只能有 Cons 和 Nil 两个子类,避免它的封装被打破。接下来我们还可以给它添加一些有趣的方法,让它更像一个 List:
1 | public sealed interface List<T> { |
我们为 List 添加了一个默认的 forEach,这样我们就可以很轻松地迭代它了。为了方便创建 List 的实例,我们再给它添加一个便捷的方法:
1 | public sealed interface List<T> { |
接下来给出一个简单的用例:
1 | public static void main(String[] args) { |
顺便提一句,用 Kotlin 实现这段逻辑可不要简单太多:
1 | sealed class List<out T> { |
好啦,有关密封类的更新我们就介绍这么多。
关于作者
霍丙乾 bennyhuo,Kotlin 布道师,Google 认证 Kotlin 开发专家(Kotlin GDE);《深入理解 Kotlin 协程》 作者(机械工业出版社,2020.6);前腾讯高级工程师,现就职于猿辅导
- GitHub:https://github.com/bennyhuo
- 博客:https://www.bennyhuo.com
- bilibili:bennyhuo不是算命的
- 微信公众号:bennyhuo