Kotlin 特色 sealed 关键字

sealed 意为密封的,可修饰类 class 和接口 interface,用来表示受限的继承结构。

Sealed Class

介绍

sealed class,密封类,密封类是一种特殊的抽象类,用于限制可以继承它的子类。

密封类具备最重要的一个特点:

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

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

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

  1. sealed class 是抽象类,可以拥有抽象方法,无法直接实例化。
  2. sealed class 的构造函数只能拥有两种可见性:默认情况下是 protected,还可以指定成 private,但不允许指定成 public。
  3. sealed class 子类可扩展局部以及匿名类以外的任意类型子类,包括普通 class、data classobject、sealed class 等,子类信息在编译期可知。
  4. sealed class 的实例,可配合 when 表达式进行判断,当所有类型覆盖后可以省略 else 分支。
  5. 当 sealed class 子类没有指定构造方法或定义任意属性的时候,建议定义成单例 object,因为即便实例化成多个实例,互相之间没有状态的区别。在 Kotlin 1.9 版本新增 data object 用于取代 object 的用法,编译器会提示“‘sealed’ sub-object can be converted to ‘data object’ ”,toString() 不会打印 HashCode 等无用信息让输出更有意义。

使用

可以在 sealed class 内部定义子类:

sealed class Language {//定义在内部data object English : Language()data class French(val str: String) : Language()class German(str: String) : Language()
}

还可以在外部定义子类:

sealed class Language {//定义在内部data object English : Language()data class French(val str: String) : Language()class German(str: String) : Language()
}//定义在同一文件中
data object Chinese : Language()
data class Japanese(val str: String) : Language()

此外还可以定义在同包名不同一文件下

//定义在同包名不同一文件下
data class Korean(val str: String) : Language()

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

  • 判断 sealed class 内部子类类型自然需要指定父类前缀
  • 判断 sealed class 外部子类类型自然无需指定前缀
  • object class 的话可以直接进行实例判断,也可以用 is 关键字判断类型匹配
  • 普通 class 类型的话则必须使用 is 关键字判断类型匹配
    fun demo(language: Language) {when (language) {Language.English -> {}is Language.French -> {}is Language.German -> {}Chinese -> {}is Japanese -> {}is Korean -> {}}}

我们知道Kotlin代码最终会编译成Java字节码的,让我们来看一下,上述的Kotlin代码反编译之后是怎么样的:

public abstract class Language {public /* synthetic */ Language(DefaultConstructorMarker defaultConstructorMarker) {this();}private Language() {}// subclass:data objectpublic static final class English extends Language {public static final English INSTANCE = new English();public boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof English)) {return false;}English english = (English) obj;return true;}public int hashCode() {return -765073291;}public String toString() {return "English";}private English() {super(null);}}// subclass:data classpublic static final class French extends Language {private final String str;public static /* synthetic */ French copy$default(French french, String str2, int i, Object obj) {if ((i & 1) != 0) {str2 = french.str;}return french.copy(str2);}public final String component1() {return this.str;}public final French copy(String str2) {Intrinsics.checkNotNullParameter(str2, "str");return new French(str2);}public boolean equals(Object obj) {if (this == obj) {return true;}return (obj instanceof French) && Intrinsics.areEqual(this.str, ((French) obj).str);}public int hashCode() {return this.str.hashCode();}public String toString() {return "French(str=" + this.str + ')';}public French(String str2) {super(null);Intrinsics.checkNotNullParameter(str2, "str");this.str = str2;}public final String getStr() {return this.str;}}// subclass:classpublic static final class German extends Language {public German(String str) {super(null);Intrinsics.checkNotNullParameter(str, "str");}}
}

可以看到 sealed class 本身被编译为 abstract class,其内部子类按类型有所不同:

  • data object 类型在 class 内部集成了静态的 INSTANCE 实例,同时还有equalstoString 以及 hashCode 函数。
  • data class 类型在 class 内部集成了属性的 getequalscopytoString 以及 hashCode 函数。
  • class 类型仍是普通的 class。

而外部子类和同包名不同一文件下的子类则自然是定义在 Language 抽象类外部,内容也是跟上面一样的。

Sealed Interface

sealed interface,密封接口,和 sealed class 有几乎一样的特点。此外,还有额外的优势,可以帮助密封类、枚举类等类实现多继承和扩展性,比如搭配枚举,以处理更复杂的分类逻辑。

举例:Flappy Bird 游戏的过程中会产生很多 Action 来触发数据的计算以推动 UI 刷新以及游戏的进程,Action 可以用 enum class 来管理。

其中有些 Action 是关联的,有些则没有关联、不是同一层级。但是 enum class 默认扩展自 Enum 类,无法再嵌套 enum。

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

 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 -> {}Action.Start -> {}Action.Exit -> {}Action.Restart -> {}Action.Up -> {}Action.Down -> {}Action.HitGround -> {}Action.HitPipe -> {}Action.CrossedPipe -> {}Action.Move -> {}Action.Reset -> {}Action.RoadMove -> {}Action.RoadReset -> {}}}

借助 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 -> {}is GameAction -> {when (action) {GameAction.Start -> {}GameAction.Exit -> {}GameAction.Restart -> {}}}is BirdAction -> {when (action) {BirdAction.Up -> {}BirdAction.Down -> {}else -> {}}}is PipeAction -> {when (action) {PipeAction.Move -> {}PipeAction.Reset -> {}}}is RoadAction -> {when (action) {RoadAction.Move -> {}RoadAction.Reset -> {}}}}}

sealed 与 enum 的区别

  • enum:enum 只是一个值(常量),每个 enum 常量只能以单例的形式存在。
  • sealed:sealed可以是一个值(定义成 data object 不携带数据),还可以是一个有状态的值(定义成 data class 携带数据)。sealed class 子类可以拥有多个实例,不受限制,每个均可以拥有自己的状态。
  • enum class 不能扩展自 sealed class 以及其他任何 Class,但可以实现 sealed interface,正如上面 Action 的举例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/19492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编:x86汇编环境搭建与基础框架(32位)

32位汇编代码编写环境:Visual Studio(笔者用的版本为2017);先来说一下在Visual Studio 2017中编写汇编代码的准备操作: ①创建空项目 ②设置项目属性:平台工具集设置为Visual Studio 2015(v140)&#xff0…

YOLOv5改进 | 注意力机制 | 添加三重注意力机制 TripletAttention【原理 + 完整代码】

💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 得益于在通道或空间位置之间建立相互依赖关系的能力,近年来,注意力机制在计算机视觉任务中得到了广泛的研究和应用。…

嵌入式Linux命令基础

一、命令概述 1. 命令本质 命令的特性:一般就是对应shell命令,每一个命令代表一个可执行程序,运行一个命令就相当于 运行一个可执行代码。 2. 打开终端方法 第一种方法:通过鼠标右键选择打开终端 第二种方法:利用…

Django——Admin站点(Python)

#前言: 该博客为小编Django基础知识操作博客的最后一篇,主要讲解了关于Admin站点的一些基本操作,小编会继续尽力更新一些优质文章,同时欢迎大家点赞和收藏,也欢迎大家关注等待后续文章。 一、简介: Djan…

存储型XSS攻击:原理、示例与防御策略

引言 在网络安全的众多威胁中,跨站脚本攻击(XSS)因其对用户隐私和网站安全的严重威胁而备受关注。存储型XSS作为XSS的一种,其特点是恶意脚本被存储在目标服务器上,这使得攻击具有更高的隐蔽性和持久性。本文将深入探讨…

认识Oracle v$mystat视图

v$mystat就是当前用户的各种统计信息, sid就是session的id(也就是当前用户),STATISTIC#就是统计量的编号(用来唯一确定统计量的名称),value是统计量的值; desc命令在Oracle中通常用于查看表结构; v$mystat视图中只会有当前用户…

【NVM】nvm常用命令,切换node版本命令

nvm常用的命令,切换node版本命令 nvm 查看支持安装的node版本 nvm list available nvm安装指定版本node nvm install 版本号 例如:nvm install 10.24.1 nvm查看本机安装所有node版本 nvm list nvm切换node版本 nvm use 10.24.1 检测当前node版本 node -…

大数据中的电商数仓项目:探秘业务的核心

我学习完一个电商数仓的项目和电影实时推荐项目,便兴冲冲的去面试大数据开发岗,在面试的时候,面试官总是喜欢问,聊聊你为什么要做这个项目以及你这个项目有哪些业务? 我心想,为什么要做这个业务&#xff1f…

【码银送书第二十期】《游戏运营与出海实战:策略、方法与技巧》

市面上的游戏品种繁杂,琳琅满目,它们是如何在历史的长河中逐步演变成今天的模式的呢?接下来,我们先回顾游戏的发展史,然后按照时间轴来叙述游戏运营的兴起。 作者:艾小米 本文经机械工业出版社授权转载&a…

用Idea 解决Git冲突

https://intellijidea.com.cn/help/idea/resolving-conflicts.html https://www.jetbrains.com/help/idea/resolve-conflicts.html idea 官方文档 当您在团队中工作时,您可能会遇到这样的情况:有人对您当前正在处理的文件进行更改。如果这些更改没有重叠(也就是说…

Ps系统教程03

选区工具的组合使用 先用魔棒将大致区域点击圈主 会发现一些零散的小区域 使用套索工具进行区域的加减(按住shift/alt键进行相关区域加减) 可以放大查看 基本处理完细节之后 如果把不用的填充背景直接按delete删除,那么原版图案就会…

Hadoop3:MapReduce的序列化和反序列化

一、概念 1、序列化 就是把内存中的对象,转换成字节序列 (或其他数据传输协议)以便于存储到磁 盘(持久化)和网络传输。 2、反序列化 就是将收到字节序列(或其他数据传输协议)或者是磁盘的持…

前端最简易实现特定区域全屏展示

一、前言 在日常开发项目中,通常会遇到大屏展示的表格,或是想要将某一表格或内容全部展示;类似于快捷键F11的功能。遇到这种需求通常是要想办法使用插件来全屏展示,还需要改特定结构来实现; 下面介绍一种简易方式&am…

LeetCode-47 全排列Ⅱ

LeetCode-47 全排列Ⅱ 题目描述解题思路代码说明 题目描述 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 : 输入:nums [1,1,2]输出: [[1,1,2], [1,2,1], [2,1,1]] b站题目解读讲的不好&…

部署k8s的DashBoard

1. 部署 Dashboard UI [rootk8s-master ~]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recomme nded.yaml一般上面的网站访问不了 可以下载我上传的资源DashBoard的recommended.yaml vim recommended.yaml 复制粘贴我上…

做外贸,怎么选国外服务器?

不管是新手还是外贸老司机,大家都知道要用海外服务器来做外贸网站,无论外贸独立站的客户是欧美、东南亚、还是非洲,都不能选择国内机房的服务器,必须选择海外服务器,这是共识。 但是今天,我要告诉大家一个…

Java Apache Jaccard文本相似度匹配初体验

文章目录 前言一、文本相似度算法的选择二、常见的文本相似度算法介绍三、使用示例1、引入jar包2、方法示例3、Jaccard源码剖析4、Jaccard源码解释 写在最后 前言 产品今天提了个需求,大概是这样的,来,请看大屏幕。。。额。。。搞错了&#…

Spring Boot 2 入门基础

学习要求 ● 熟悉Spring基础 ● 熟悉Maven使用 环境要求 ● Java8及以上 ● Maven 3.3及以上:https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started-system-requirements 学习资料 ● 文档地址: htt…

前端从零到一开发vscode插件并发布到插件市场

前端从零到一开发vscode插件并发布到插件市场 背景目标成果展示一条龙实现过程安装插件脚手架和工具创建项目运行调试打包第一次打包前的必要操作 发布第一次发布前账号准备注册Azure DevOps发布账号-获取token注册vscode开发者账号终端登录vsce 发布方式2-手动上传插件 进阶开…

深入分析 Android Service (三)

文章目录 深入分析 Android Service (三)1. Service 与 Activity 之间的通信2. 详细示例:通过绑定服务进行通信2.1 创建一个绑定服务2.2 绑定和通信 3. 优化建议4. 使用场景5. 总结 深入分析 Android Service (三) 1. Service 与 Activity 之间的通信 在 Android …