Kotlin 特色之 Sealed Class 和 Interface

article/2025/10/26 6:00:37

6083c358fd01a3e51f7d7475d2c5ef7e.jpeg

/   今日科技快讯   /

近日,我国在太原卫星发射中心使用长征六号改运载火箭,成功将云海三号卫星发射升空,卫星顺利进入预定轨道,发射任务获得圆满成功。该卫星主要用于开展大气海洋环境要素探测、空间环境探测、防灾减灾和科学试验等。此次任务是长征系列运载火箭的第448次飞行。

/   作者简介   /

本篇文章转自TechMerger的博客,文章主要分享了 Kotlin 中 Sealed class 的相关内容,相信会对大家有所帮助!

原文地址:

https://juejin.cn/post/7160111185201725476

/   前言   /

sealed class 以及 1.5 里新增的 sealed interface 可谓是 Kotlin 语言的一大特色,其在类型判断、扩展和实现的限制场景里非常好用。

本文将从特点、场景和原理等角度综合分析 sealed 语法。

  • Sealed Class

  • Sealed Interface

  • Sealed Class & Interface VS Enum

  • Sealed Class VS Interface

/   Sealed class   /

sealed class,密封类。具备最重要的一个特点:

其子类可以出现在定义 sealed class 的不同文件中,但不允许出现在与不同的 module 中,且需要保证 package 一致

这样既可以避免 sealed class 文件过于庞大,又可以确保第三方库无法扩展你定义的 sealed class,达到限制类的扩展目的。事实上在早期版本中,只允许在 sealed class 内部或定义的同文件内扩展子类,这些限制在 Kotlin 1.5 中被逐步放开。

如果在不同 module 或 package 中扩展子类的话,IDE 会显示如下的提示和编译错误:

Inheritor of sealed class or interface declared in package xxx but it must be in package xxx where base class is declared

sealed class 还具有如下特点或限制:

sealed class 是抽象类,可以拥有抽象方法,无法直接实例化。否则,编译器将提示如下:

Sealed types cannot be instantiated

sealed class 的构造函数只能拥有两种可见性:默认情况下是 protected,还可以指定成 private,public 是不被允许的。

Constructor must be private or protected in sealed class

sealed class 子类可扩展局部以及匿名类以外的任意类型子类,包括普通 class、data class、object、sealed class 等,子类信息在编译期可知。假使匿名类扩展自 sealed class 的话,会弹出错误提示:

This type is sealed, so it can be inherited by only its own nested classes or objects

sealed class 的实例,可配合 when 表达式进行判断,当所有类型覆盖后可以省略 else 分支。如果没有覆盖所有类型,也没有 else 统筹则会发生编译警告或错误

1.7 以前

Non-exhaustive 'when' statements on sealed class/interface will be prohibited in 1.7.

1.7 及以后

'when' expression must be exhaustive, add ...

当 sealed class 没有指定构造方法或定义任意属性的时候,建议子类定义成单例,因为即便实例化成多个实例,互相之间没有状态的区别:

'sealed' subclass has no state and no overridden 'equals()'

下面结合代码看下 sealed class 的使用和原理。

示例代码:

// TestSealed.ktsealed class GameAction(times: Int) {// Inner of Sealed Classobject Start : GameAction(1)data class AutoTick(val time: Int) : GameAction(2)class Exit : GameAction(3)}

除了在 sealed class 内嵌套子类外,还可以在外部扩展子类:

// TestSealed.ktsealed class GameAction(times: Int) {...}// Outer of Sealed Classobject Restart : GameAction(4)

除了可以在同文件下 sealed class 外扩展子类外,还可以在同包名不同文件下扩展。

// TestExtendedSealedClass.kt// Outer of Sealed Class fileclass TestExtendedSealedClass: GameAction(5)

对于不同类型的扩展子类,when 表达式的判断亦不同:

  • 判断 sealed class 内部子类类型自然需要指定父类前缀

  • object class 的话可以直接进行实例判断,也可以用 is 关键字判断类型匹配

  • 普通 class 类型的话则必须加上 is 关键字

  • 判断 sealed class 外部子类类型自然无需指定前缀

class TestSealed {fun test(gameAction: GameAction) {when (gameAction) {GameAction.Start -> {}// is GameAction.Start -> {}is GameAction.AutoTick -> {}is GameAction.Exit -> {}Restart -> {}is TestExtendedSealedClass -> {}}}}

如下反编译的 Kotlin 代码可以看到 sealed class 本身被编译为 abstract class。

扩展自其的内部子类按类型有所不同:

  • object class 在 class 内部集成了静态的 INSTANCE 实例

  • 普通 class 仍是普通 class

  • data Class 则是在 class 内部集成了属性的 get、toString 以及 hashCode 函数

public abstract class GameAction {private GameAction(int times) { }public GameAction(int times, DefaultConstructorMarker $constructor_marker) {this(times);}// subclass:objectpublic static final class Start extends GameAction {@NotNullpublic static final GameAction.Start INSTANCE;private Start() {super(1, (DefaultConstructorMarker)null);}static {GameAction.Start var0 = new GameAction.Start();INSTANCE = var0;}}// subclass:classpublic static final class Exit extends GameAction {public Exit() {super(3, (DefaultConstructorMarker)null);}}// subclass:data classpublic static final class AutoTick extends GameAction {private final int time;public final int getTime() {return this.time;}public AutoTick(int time) {super(2, (DefaultConstructorMarker)null);this.time = time;}...@NotNullpublic String toString() {return "AutoTick(time=" + this.time + ")";}public int hashCode() { ... }public boolean equals(@Nullable Object var1) { ... }}}
而外部子类则自然是定义在 GameAction 抽象类外部。
public abstract class GameAction {...}public final class Restart extends GameAction {@NotNullpublic static final Restart INSTANCE;private Restart() {super(4, (DefaultConstructorMarker)null);}static {Restart var0 = new Restart();INSTANCE = var0;}}

文件外扩展子类可想而知。

public final class TestExtendedSealedClass extends GameAction {public TestExtendedSealedClass() {super(5, (DefaultConstructorMarker)null);}}

/   Sealed Interface   /

sealed interface 即密封接口,和 sealed class 有几乎一样的特点。比如:

限制接口的实现:一旦含有包含 sealed interface 的 module 经过了编译,就无法再有扩展的实现类了,即对其他 module 隐藏了接口。

还有些额外的优势:

帮助密封类、枚举类等类实现多继承和扩展性,比如搭配枚举,以处理更复杂的分类逻辑。

Additionally, sealed interfaces enable more flexible restricted class hierarchies because a class can directly inherit more than one sealed interface.

比如 Flappy Bird 游戏的过程中会产生很多 Action 来触发数据的计算以推动 UI 刷新以及游戏的进程,Action 可以用 enum class 来管理。其中有些 Action 是关联的,有些则没有关联、不是同一层级。但是 enum class 默认扩展自 Enum 类,无法再嵌套 enum。

Enum class cannot inherit from classes

这将导致层级混乱、阅读性不佳,甚至有的时候功能相近的时候还得特意取个不同的名称。

enum class Action {Tick,// GameActionStart, Exit, Restart,// BirdActionUp, Down, HitGround, HitPipe, CrossedPipe,// PipeActionMove, Reset,// RoadAction// 防止和 Pipe 的 Action 重名导致编译出错,// 将功能差不多的 Road 移动和重置 Action 定义加上了前缀RoadMove, RoadReset}fun dispatch(action: Action) {when (action) {Action.Tick -> TODO()Action.Start -> TODO()Action.Exit -> TODO()Action.Restart -> TODO()Action.Up -> TODO()Action.Down -> TODO()Action.HitGround -> TODO()Action.HitPipe -> TODO()Action.CrossedPipe -> TODO()Action.Move -> TODO()Action.Reset -> TODO()Action.RoadMove -> TODO()Action.RoadReset -> TODO()}}

借助 sealed interface 我们可以给抽出 interface,并将 enum 进行层级拆分。更加清晰、亦不用担心重名。

sealed interface Actionenum class GameAction : Action {Start, Exit, Restart}enum class BirdAction : Action {Up, Down, HitGround, HitPipe, CrossedPipe}enum class PipeAction : Action {Move, Reset}enum class RoadAction : Action {Move, Reset}object Tick: Action

使用的时候就可以对抽成的 Action 进行嵌套判断:

fun dispatch(action: Action) {when (action) {Tick -> TODO()is GameAction -> {when (action) {GameAction.Start -> TODO()GameAction.Exit -> TODO()GameAction.Restart -> TODO()}}is BirdAction -> {when (action) {BirdAction.Up -> TODO()BirdAction.Down -> TODO()else -> TODO()}}is PipeAction -> {when (action) {PipeAction.Move -> TODO()PipeAction.Reset -> TODO()}}is RoadAction -> {when (action) {RoadAction.Move -> TODO()RoadAction.Reset -> TODO()}}}}

/   总结   /

Sealed Class & Interface VS Enum

总体来说 sealed class 和 interface 和 enum 有相近的地方,也有明显区别,需要留意:

  • 每个 enum 常量只能以单例的形式存在

  • sealed class 子类可以拥有多个实例,不受限制,每个均可以拥有自己的状态

  • enum class 不能扩展自 sealed class 以及其他任何 Class,但他们可以实现 sealed 等 interface


Sealed Class VS Interface

Sealed classes and interfaces represent restricted class hierarchies that provide more control over inheritance.

sealed class 和 interface 都意味着受限的类层级结构,便于在继承和实现上进行更多控制。具备如下的共同特性:

其 sub class 需要定义在同一 Module 以及同一 package,不局限于 sealed 内部或同文件内。

看下对比:

ba37c0665a5e7cc01c65a826097af74d.jpeg

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Android Drawable妙用和mutate()源码解析

Kotlin Flow响应式编程,基础知识入门

欢迎关注我的公众号

学习技术或投稿

a1530c24a6571934948d2f2adc38692b.png

22f254f533ffcd5cc1bef47f1231e326.jpeg

长按上图,识别图中二维码即可关注


http://chatgpt.dhexx.cn/article/7gataCq6.shtml

相关文章

c#中sealed关键字的使用

c#中sealed关键字 前几天给大家总结了virtual(虚拟的)、abstract(抽象的)、override(重写)关键字,今天就抽空把sealed(密封的)给大家总结出来,因为这四个是一…

快速了解sealed关键字

sealed关键字 sealed的英文意思就是密封,禁止的意思。在c#中sealed关键字可以用来修饰类和方法。作用于类时不能和abstract对象一起使用,因为使用了sealed修饰类表示此类不能被继承,而抽象类不能被继承将毫无意义。代码如下: publ…

PS 解决不能完成命令,暂存盘空间不足的问题

PS 解决不能完成命令,暂存盘空间不足的问题 解决方法: 在PS启动之时,按住 ctrl alt 键 选择暂存盘设置(旁边的上下箭头设置优先级) 方法如果没有弹出这个暂存盘首选项 可以用下列办法了实现修改 这个暂存盘设置 按…

GeoServer发布数据库中的空间数据表

如何利用GeoServer发布PostgreSQL/PostGIS中的空间数据 1.矢量数据发布2.栅格数据发布2.1 安装Image Mosaic JDBC扩展2.2 准备金字塔和瓦片2.3 创建配置文件2.4 创建数据库2.5 发布数据2.6 可能遇到的坑 实际工作中,数据存储在本地的情况应该不多,大多数…

【数据结构】顺序表的实现

文章目录 一、顺序表概念及结构二、动态顺序表和静态顺序表的选择三、动态顺序表的实现逻辑(1)创建结构体(2)具体函数实现(*)顺序表初始化(*)释放顺序表(*)打…

mysql:详解创建表的常用数据类型

1.什么是数据类型 数据类型是指列、存储过程参数、表达式和局部变量的数据特征,它决定了数据的存储格式,代表了不同的信息类型。 有一些数据是要存储为数字的,数字当中有些是要存储为整数、小数、日期型等... 2.mysql常见数据类型 整数型浮…

群晖服务器创建文件夹,群晖Synology 创建共享文件夹视频图文教程

群晖 Synology 是 NAS网络存储服务器,虽然它的操作大部分都跟 Windows 差不多,但是还是有些许不同。今天我们就来学习学习如何创建群晖 Synology NAS网络存储服务器的共享文件夹。共享文件夹,可以理解为在 Windows 上的 C 、D、E盘或者是盘中的根目录。由于群晖 Synology NA…

【Windows10】Win10存储空间的作用以及如何创建存储空间

本文目录 一、不得不说的话 二、存储空间的作用 三、如何创建存储空间 四、特别提示 一、不得不说的话 默认情况下, Windows10 系统的控制面板中会有一个“储存空间”选项,不过大多用户都不知道这个选项有什么作用。接下来,就在本文中具…

win10下docker安装oracle及创建表空间

1、拉取oracle镜像 docker pull jaspeen/oracle-11g 2、查看镜像 docker images 3、下载oralce安装文件并解压 4、启动镜像 docker run --privileged --name oracle11g -p 1521:1521 -v D:\oracle:/install jaspeen/oracle-11g 5、等待安装完成显示如下: 6、更…

ejb模式_EJB的完整形式是什么?

ejb模式 EJB:企业Java Bean (EJB: Enterprise Java Bean) EJB is an abbreviation of Enterprise Java Bean. EJB is one of many Java application programming interfaces (API) for flexible and manageable structuring of Java Platform, Enterprise Edition (…

到底EJB是什么?

 到底EJB是什么?被口口相传的神神秘秘的,百度一番,总觉得没有讲清楚的,仍觉得一头雾水。百度了很久,也从网络的文章的只言片语中,渐渐有了头绪。 用通俗话说,EJB就是&a…

什么是EJB?不再神秘!

1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那么,总得说说什么是所谓的"服务集群"和"企业级开发&…

jndi(是什么)和ejb容器的关系

如下: 转载了几篇关于ejb jndi的文章! 转载: http://blog.csdn.net/zhaosg198312/article/details/3979435 JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。 JNDI的扩展: JNDI在满足了数据源配置的要求的基础上&#xff…

EJB是什么?

来源:http://blog.csdn.net/jojo52013145/article/details/5783677 1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那…

EJB到底是什么

百科定义EJB: 被称为java企业bean,服务器端组件,核心应用是部署分布式应用程序。用它部署的系统不限定平台。实际上ejb是一种产品,描述了应用组件要解决的标准 标准: 可扩展 (Scalable)分布式 (Distributed)事务处理(T…

EJB是什么,以及weblogic和tomcat的区别

首先说一下weblogic和tomcat的区别 weblogic是 java应用服务器 用于开发、集成、部署和管理大型分布式web应用、网络应用和数据库应用 将java的动态功能和java enterprise标准的安全性引入大型网络应用的开发集成部署和管理之中。 weblogic中有domain ,域是作为一…

到底EJB是什么

到底EJB是什么?被口口相传的神神秘秘的,百度一番,总觉得没有讲清楚的,仍觉得一头雾水。百度了很久,也从网络的文章的只言片语中,渐渐有了头绪。 用通俗话说,EJB就是:"把你编写的…

EJB是什么

1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那么,总得说说什么是所谓的"服务 集群"和"企业级开发…

EJB到底是什么,真的那么神秘吗?

百科定义EJB: 被称为java企业bean,服务器端组件,核心应用是部署分布式应用程序。用它部署的系统不限定平台。实际上ejb是一种产品,描述了应用组件要解决的标准 标准: 可扩展 (Scalable)分布式 (Distributed)事务处理(T…

程序无法正常启动(0xc000007b) 解决的过程

版权声明:转载需标明该文链接。 https://blog.csdn.net/zaibeijixing/article/details/87785073 解决(2019/2/20) 之前基于opencv 的项目都完全没问题,现在新建的一个opencv程序出现上述错误。查阅网上资料,自己摸索&a…