Kotlin 密封类进化了

这是 Sealed Classes(密封类)系列的第三篇文章,之前的文章从原理、优化、实战以不同的角度分别介绍了 Sealed Classes 的强大。

Kotlin Sealed 是什么?为什么 Google 都在用 文章中主要包含以下内容:

  • Sealed Classes 原理分析?
  • 枚举、抽象类、Sealed Classes 分别有那些优缺点?
  • 分别在什么情况下使用枚举和 Sealed Classes?
  • 为什么 Sealed Classes 用于表示受限制的类层次结构?
  • 在项目中如何使用 Sealed Classes?
  • 禁止在 Sealed Classes 所定义的文件外使用, Kotlin 是如何做到的呢?
  • ......

Kotlin 中的密封类 优于 带标签的类 文章中主要包含以下内容:

  • 什么是 Tagged Classes(标记类)?
  • Tagged Classes 的优缺点,以及在项目中所带来的影响?
  • 如何使用 Sealed Classes 优化现有的代码,可以带来那些收益?

而这篇文章主要来介绍 Sealed Classes 在 Kotlin 1.5.0 上带来的新特性及原理分析,在开始分析之前,先来简单的回顾一下之前文章的内容。

Sealed Classes

什么是 Sealed Classes?

Sealed Classes 用于表示受限制的类层次结构,其实这句话可以拆成两句话来理解。

  • Sealed Classes 用于表示层级关系: 子类可以是任意的类, 数据类、Kotlin 对象、普通的类,甚至也可以是另一个 Sealed
  • Sealed Classes 受限制: 必须在同一文件中,或者在 Sealed Classes 内部中使用,在 Kotlin 1.1 之前,规则更加严格,子类只能在 Sealed Classes 内部中使用

Sealed Classes 的优点:

  • Sealed Classes 使类之间的职责分明,提高代码的可读性
  • 扩展性强,可以在不修改原有的代码结构的基础上添加新的参数或者子类
  • 每个类中不包含无关的字段,在一定程度上减少对象所占用的内存
  • Sealed Classes 结合 when 表达式一起使用会更加的方便,when 语句下的所有分支可以通过快捷键 Mac/Win/Linux:Alt + Enter 自动生成,效果如下所示:

这里只是简单的回顾了一下之前的内容,接下来我们一起来看看,在新版本 Kotlin 1.5.0 中 Sealed Classes 给我们带来了那些优化。

Sealed Classes 进化了

在 2021 年 5 月份,Kotlin 官方发布了 1.5.0,在这个版本中带来几个非常有用的特性,而在这篇文章我们主要介绍 Sealed Classes,更多信息请查看 What's new in Kotlin 1.5.0

在 Kotlin 1.0 时,子类只能在 Sealed Classes 内部中使用,因为 Sealed class 会被编译成 abstract class,并且默认的构造方法被私有化了,所以子类必须嵌套在 Sealed Classes 类中。

在 Kotlin 1.1 时,允许顶级的 Sealed Classes 和它顶级子类在同一个文件中,因为编译器会自动生成了一个 公有 的构造方法,在子类的构造方法中调用了父类 公有 的构造方法,而这些都是 Kotlin 编译器帮我们做的。

在 Kotlin 1.5.0 中,放宽了对 Sealed Classes 限制,只需要保证 Sealed Classes 和它的子类,在同一个包名和 module 下面即可,这些都是 Kotlin 编译器帮我们做的。

接下里我们一起来分析一下为什么 Kotlin 需要升级 Sealed Classes?在之前的文章 Kotlin 中的密封类 优于 带标签的类 分析了 Sealed Classes 有很多优点,但是它也有很多不足之处,这也是为什么需要升级 Sealed Classes 的原因:

  • Sealed Classes 的子类,被限制在单个父类中
  • 把所有的代码放在一个文件中,会造成文件臃肿,可读性下降,如下所示

sealed class Color {
    class Red : Color()
    class Blue : Color()
    // ...... 更多的颜色
}

sealed class Figure {
    abstract fun draw()
}

class Rectangle(val color: Color) : Figure() {
    override fun draw() {
    }
}

class Round(val color: Color) : Figure() {
    override fun draw() {

    }
}

class Triangle(val color: Color) : Figure() {
    override fun draw() {

    }
}

...... // 随着需求的增加,文件会越发庞大

// 通过快捷键 Mac/Win/Linux:Alt + Enter 自动生成
fun drawFigure(figure: Figure) {
    when (figure) {
        is Rectangle -> TODO()
        is Round -> TODO()
        is Triangle -> when (figure.color) {
            is Color.Blue -> TODO()
            is Color.Red -> TODO()
        }
    }
}

在 Kotlin 1.1 中被限制在一个文件中,随着需求的增加,文件只会越发的庞大,现在放宽了限制之后,我们可以将子类拆分成单独的文件,可以进一步的提高代码的可读性。

  • 只允许顶级的 Sealed Classes 和它顶级的子类在同一个文件中,对于非顶级的 Sealed Classes,所有子类都应该在其内部声明,以下代码编译会失败

正如你所见 Sealed Classes 还是不够灵活,非顶级子类 Yellow 定在 Color 的外面,编译就会出错,而在 Koltin 1.5.0 放宽了限制之后,以上代码可以正常编译通过,不仅提高代码的可读性,而且灵活性也提高了。

除此之外还允许在 Sealed Classes 中声明受保护的构造函数。在 1.5.0 之前所有 Sealed Classes 的默认构造函数都是 private,但是在 1.5.0 之后默认构造函数都是 protected。

sealed class Figure {
    constructor() // 默认为 protected
    private constructor(area: Double) : this()
}

但是不允许声明为 public,因为编译器会自动生成,如果声明为 public 编译就会出错:

一起来看看声明为 protected 的构造函数,反编译后的 Java 代码都做了什么。Tools → Kotlin → Show Kotlin Bytecode

public abstract class Figure {
   private Figure() {
   }

   private Figure(double area) {
      this();
   }

   // $FF: synthetic method
   public Figure(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   // $FF: synthetic method
   public Figure(double area, DefaultConstructorMarker $constructor_marker) {
      this(area);
   }
}

public final class Rectangle extends Figure {
   public Rectangle() {
      super(1.0D, (DefaultConstructorMarker)null);
   }
}

正如你所见,生成的构造方法还是私有的,只不过编译器会自动生成 公有 的构造方法,在子类的构造方法中调用了父类 公有 的构造方法。除此变化之外,另外还有一个重要的特性,增加了密封接口(Sealed Interface)。

注意:

如果你已经升级到 Kotlin 1.5.0,编译器还提示出错,请将下面的代码添加在 build.gradle 中。

compileKotlin {
    kotlinOptions {
        languageVersion = "1.5"
    }
}

Sealed Interface

Sealed Interface 和 Sealed Classes 一样都是用于表示受限制的类层次结构,所以它也拥有 Sealed Classes 所有的优点,Sealed Classes 拥有的特性 Sealed Interface 也都拥有。

但是不同之处在于 Sealed Classes 被限制在单个父类中,而 Sealed Interface 支持更灵活的受限制类层次结构,因为子类可以实现多个 Sealed Interface,如下所示。

// IColor.kt
sealed interface IColor
class Red : IColor
class Blue : IColor

// IArea.kt
sealed interface IArea {
    fun area(): Double
}

// IFigure.kt
sealed interface IFigure

class Round() : IFigure, IColor

class Rectangle(val length: Double, val width: Double) : IFigure, IArea, IColor {
    override fun area(): Double = length * width
}

Sealed Interface 允许子类有多个实现,自由度更高,使得代码更加的灵活,但是它和 Sealed Classes 一样,被限制在了同一个包名和 module 下面,如果违反这个限制编译就会出错。

关于 Sealed Interface 相关的内容,就先介绍到这里,这篇文章主要分析了 Sealed Classes 以及 Sealed Interface 优缺点,在 Kotlin 1.5.0 中还增加了其他的特性,将会在后续的文章中介绍。

全文到这里就结束了,如果有帮助 点个赞 就是对我最大的鼓励
代码不止,文章不停


最后推荐我一直在更新维护的项目和网站:

  • 计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看:AndroidX-Jetpack-Practice

  • LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程 题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析

  • 剑指 offer 及国内外大厂面试题解:在线阅读

  • LeetCode 系列题解:在线阅读

  • 最新 Android 10 源码分析系列文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,仓库持续更新,欢迎前去查看 Android10-Source-Analysis

  • 整理和翻译一系列精选国外的技术文章,每篇文章都会有译者思考部分,对原文的更加深入的解读,仓库持续更新,欢迎前去查看 Technical-Article-Translation

  • 「为互联网人而设计,国内国外名站导航」涵括新闻、体育、生活、娱乐、设计、产品、运营、前端开发、Android 开发等等网址,欢迎前去查看 为互联网人而设计导航网站

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容