Kotlin 密封類進(jìn)化了

這是 Sealed Classes(密封類)系列的第三篇文章,之前的文章從原理寸士、優(yōu)化捡遍、實(shí)戰(zhàn)以不同的角度分別介紹了 Sealed Classes 的強(qiáng)大砸王。

Kotlin Sealed 是什么?為什么 Google 都在用 文章中主要包含以下內(nèi)容:

  • Sealed Classes 原理分析葱淳?
  • 枚舉钝腺、抽象類、Sealed Classes 分別有那些優(yōu)缺點(diǎn)赞厕?
  • 分別在什么情況下使用枚舉和 Sealed Classes艳狐?
  • 為什么 Sealed Classes 用于表示受限制的類層次結(jié)構(gòu)?
  • 在項(xiàng)目中如何使用 Sealed Classes皿桑?
  • 禁止在 Sealed Classes 所定義的文件外使用毫目, Kotlin 是如何做到的呢蔬啡?
  • ......

Kotlin 中的密封類 優(yōu)于 帶標(biāo)簽的類 文章中主要包含以下內(nèi)容:

  • 什么是 Tagged Classes(標(biāo)記類)?
  • Tagged Classes 的優(yōu)缺點(diǎn)镀虐,以及在項(xiàng)目中所帶來(lái)的影響箱蟆?
  • 如何使用 Sealed Classes 優(yōu)化現(xiàn)有的代碼,可以帶來(lái)那些收益刮便?

而這篇文章主要來(lái)介紹 Sealed Classes 在 Kotlin 1.5.0 上帶來(lái)的新特性及原理分析空猜,在開始分析之前,先來(lái)簡(jiǎn)單的回顧一下之前文章的內(nèi)容恨旱。

Sealed Classes

什么是 Sealed Classes辈毯?

Sealed Classes 用于表示受限制的類層次結(jié)構(gòu),其實(shí)這句話可以拆成兩句話來(lái)理解窖杀。

  • Sealed Classes 用于表示層級(jí)關(guān)系: 子類可以是任意的類漓摩, 數(shù)據(jù)類、Kotlin 對(duì)象入客、普通的類管毙,甚至也可以是另一個(gè) Sealed
  • Sealed Classes 受限制: 必須在同一文件中,或者在 Sealed Classes 內(nèi)部中使用桌硫,在 Kotlin 1.1 之前夭咬,規(guī)則更加嚴(yán)格,子類只能在 Sealed Classes 內(nèi)部中使用

Sealed Classes 的優(yōu)點(diǎn):

  • Sealed Classes 使類之間的職責(zé)分明铆隘,提高代碼的可讀性
  • 擴(kuò)展性強(qiáng)卓舵,可以在不修改原有的代碼結(jié)構(gòu)的基礎(chǔ)上添加新的參數(shù)或者子類
  • 每個(gè)類中不包含無(wú)關(guān)的字段,在一定程度上減少對(duì)象所占用的內(nèi)存
  • Sealed Classes 結(jié)合 when 表達(dá)式一起使用會(huì)更加的方便膀钠,when 語(yǔ)句下的所有分支可以通過(guò)快捷鍵 Mac/Win/Linux:Alt + Enter 自動(dòng)生成掏湾,效果如下所示:

這里只是簡(jiǎn)單的回顧了一下之前的內(nèi)容,接下來(lái)我們一起來(lái)看看肿嘲,在新版本 Kotlin 1.5.0 中 Sealed Classes 給我們帶來(lái)了那些優(yōu)化融击。

Sealed Classes 進(jìn)化了

在 2021 年 5 月份,Kotlin 官方發(fā)布了 1.5.0雳窟,在這個(gè)版本中帶來(lái)幾個(gè)非常有用的特性尊浪,而在這篇文章我們主要介紹 Sealed Classes,更多信息請(qǐng)查看 What's new in Kotlin 1.5.0封救。

在 Kotlin 1.0 時(shí)拇涤,子類只能在 Sealed Classes 內(nèi)部中使用,因?yàn)?Sealed class 會(huì)被編譯成 abstract class誉结,并且默認(rèn)的構(gòu)造方法被私有化了鹅士,所以子類必須嵌套在 Sealed Classes 類中。

在 Kotlin 1.1 時(shí)惩坑,允許頂級(jí)的 Sealed Classes 和它頂級(jí)子類在同一個(gè)文件中如绸,因?yàn)榫幾g器會(huì)自動(dòng)生成了一個(gè) 公有 的構(gòu)造方法嘱朽,在子類的構(gòu)造方法中調(diào)用了父類 公有 的構(gòu)造方法,而這些都是 Kotlin 編譯器幫我們做的怔接。

在 Kotlin 1.5.0 中搪泳,放寬了對(duì) Sealed Classes 限制,只需要保證 Sealed Classes 和它的子類扼脐,在同一個(gè)包名和 module 下面即可岸军,這些都是 Kotlin 編譯器幫我們做的。

接下里我們一起來(lái)分析一下為什么 Kotlin 需要升級(jí) Sealed Classes瓦侮?在之前的文章 Kotlin 中的密封類 優(yōu)于 帶標(biāo)簽的類 分析了 Sealed Classes 有很多優(yōu)點(diǎn)艰赞,但是它也有很多不足之處,這也是為什么需要升級(jí) Sealed Classes 的原因:

  • Sealed Classes 的子類肚吏,被限制在單個(gè)父類中
  • 把所有的代碼放在一個(gè)文件中方妖,會(huì)造成文件臃腫,可讀性下降罚攀,如下所示

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() {

    }
}

...... // 隨著需求的增加党觅,文件會(huì)越發(fā)龐大

// 通過(guò)快捷鍵 Mac/Win/Linux:Alt + Enter 自動(dòng)生成
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 中被限制在一個(gè)文件中,隨著需求的增加斋泄,文件只會(huì)越發(fā)的龐大杯瞻,現(xiàn)在放寬了限制之后,我們可以將子類拆分成單獨(dú)的文件炫掐,可以進(jìn)一步的提高代碼的可讀性魁莉。

  • 只允許頂級(jí)的 Sealed Classes 和它頂級(jí)的子類在同一個(gè)文件中,對(duì)于非頂級(jí)的 Sealed Classes募胃,所有子類都應(yīng)該在其內(nèi)部聲明旗唁,以下代碼編譯會(huì)失敗

正如你所見 Sealed Classes 還是不夠靈活,非頂級(jí)子類 Yellow 定在 Color 的外面痹束,編譯就會(huì)出錯(cuò)检疫,而在 Koltin 1.5.0 放寬了限制之后,以上代碼可以正常編譯通過(guò)参袱,不僅提高代碼的可讀性,而且靈活性也提高了秽梅。

除此之外還允許在 Sealed Classes 中聲明受保護(hù)的構(gòu)造函數(shù)抹蚀。在 1.5.0 之前所有 Sealed Classes 的默認(rèn)構(gòu)造函數(shù)都是 private,但是在 1.5.0 之后默認(rèn)構(gòu)造函數(shù)都是 protected企垦。

sealed class Figure {
    constructor() // 默認(rèn)為 protected
    private constructor(area: Double) : this()
}

但是不允許聲明為 public环壤,因?yàn)榫幾g器會(huì)自動(dòng)生成,如果聲明為 public 編譯就會(huì)出錯(cuò):

一起來(lái)看看聲明為 protected 的構(gòu)造函數(shù)钞诡,反編譯后的 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);
   }
}

正如你所見,生成的構(gòu)造方法還是私有的接箫,只不過(guò)編譯器會(huì)自動(dòng)生成 公有 的構(gòu)造方法攒读,在子類的構(gòu)造方法中調(diào)用了父類 公有 的構(gòu)造方法。除此變化之外辛友,另外還有一個(gè)重要的特性薄扁,增加了密封接口(Sealed Interface)。

注意:

如果你已經(jīng)升級(jí)到 Kotlin 1.5.0废累,編譯器還提示出錯(cuò)邓梅,請(qǐng)將下面的代碼添加在 build.gradle 中。

compileKotlin {
    kotlinOptions {
        languageVersion = "1.5"
    }
}

Sealed Interface

Sealed Interface 和 Sealed Classes 一樣都是用于表示受限制的類層次結(jié)構(gòu)邑滨,所以它也擁有 Sealed Classes 所有的優(yōu)點(diǎn)日缨,Sealed Classes 擁有的特性 Sealed Interface 也都擁有。

但是不同之處在于 Sealed Classes 被限制在單個(gè)父類中掖看,而 Sealed Interface 支持更靈活的受限制類層次結(jié)構(gòu)匣距,因?yàn)樽宇惪梢詫?shí)現(xiàn)多個(gè) 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 允許子類有多個(gè)實(shí)現(xiàn)墨礁,自由度更高,使得代碼更加的靈活耳峦,但是它和 Sealed Classes 一樣恩静,被限制在了同一個(gè)包名和 module 下面,如果違反這個(gè)限制編譯就會(huì)出錯(cuò)蹲坷。

關(guān)于 Sealed Interface 相關(guān)的內(nèi)容驶乾,就先介紹到這里,這篇文章主要分析了 Sealed Classes 以及 Sealed Interface 優(yōu)缺點(diǎn)循签,在 Kotlin 1.5.0 中還增加了其他的特性级乐,將會(huì)在后續(xù)的文章中介紹。

全文到這里就結(jié)束了县匠,如果有幫助 點(diǎn)個(gè)贊 就是對(duì)我最大的鼓勵(lì)
代碼不止风科,文章不停


最后推薦我一直在更新維護(hù)的項(xiàng)目和網(wǎng)站:

  • 計(jì)劃建立一個(gè)最全、最新的 AndroidX Jetpack 相關(guān)組件的實(shí)戰(zhàn)項(xiàng)目 以及 相關(guān)組件原理分析文章乞旦,正在逐漸增加 Jetpack 新成員贼穆,倉(cāng)庫(kù)持續(xù)更新,歡迎前去查看:AndroidX-Jetpack-Practice

  • LeetCode / 劍指 offer / 國(guó)內(nèi)外大廠面試題 / 多線程 題解兰粉,語(yǔ)言 Java 和 kotlin故痊,包含多種解法、解題思路玖姑、時(shí)間復(fù)雜度愕秫、空間復(fù)雜度分析

  • 劍指 offer 及國(guó)內(nèi)外大廠面試題解:在線閱讀

  • LeetCode 系列題解:在線閱讀

  • 最新 Android 10 源碼分析系列文章慨菱,了解系統(tǒng)源碼,不僅有助于分析問(wèn)題戴甩,在面試過(guò)程中符喝,對(duì)我們也是非常有幫助的,倉(cāng)庫(kù)持續(xù)更新等恐,歡迎前去查看 Android10-Source-Analysis

  • 整理和翻譯一系列精選國(guó)外的技術(shù)文章洲劣,每篇文章都會(huì)有譯者思考部分,對(duì)原文的更加深入的解讀课蔬,倉(cāng)庫(kù)持續(xù)更新囱稽,歡迎前去查看 Technical-Article-Translation

  • 「為互聯(lián)網(wǎng)人而設(shè)計(jì),國(guó)內(nèi)國(guó)外名站導(dǎo)航」涵括新聞二跋、體育战惊、生活、娛樂扎即、設(shè)計(jì)吞获、產(chǎn)品、運(yùn)營(yíng)谚鄙、前端開發(fā)各拷、Android 開發(fā)等等網(wǎng)址,歡迎前去查看 為互聯(lián)網(wǎng)人而設(shè)計(jì)導(dǎo)航網(wǎng)站

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闷营,一起剝皮案震驚了整個(gè)濱河市烤黍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌傻盟,老刑警劉巖速蕊,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異娘赴,居然都是意外死亡规哲,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門诽表,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唉锌,“玉大人,你說(shuō)我怎么就攤上這事竿奏“兰颍” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵议双,是天一觀的道長(zhǎng)痘番。 經(jīng)常有香客問(wèn)我捉片,道長(zhǎng)平痰,這世上最難降的妖魔是什么汞舱? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮宗雇,結(jié)果婚禮上昂芜,老公的妹妹穿的比我還像新娘。我一直安慰自己赔蒲,他們只是感情好泌神,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舞虱,像睡著了一般欢际。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矾兜,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天损趋,我揣著相機(jī)與錄音,去河邊找鬼椅寺。 笑死浑槽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的返帕。 我是一名探鬼主播桐玻,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荆萤!你這毒婦竟也來(lái)了镊靴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤观腊,失蹤者是張志新(化名)和其女友劉穎邑闲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梧油,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苫耸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了儡陨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褪子。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖骗村,靈堂內(nèi)的尸體忽然破棺而出嫌褪,到底是詐尸還是另有隱情,我是刑警寧澤胚股,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布笼痛,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缨伊。R本人自食惡果不足惜摘刑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刻坊。 院中可真熱鬧枷恕,春花似錦、人聲如沸谭胚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灾而。三九已至胡控,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旁趟,已是汗流浹背铜犬。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轻庆,地道東北人癣猾。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像余爆,于是被迫代替她去往敵國(guó)和親纷宇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容