Kotlin 類型層次結(jié)構(gòu)

Kotlin 的類型層次結(jié)構(gòu)需要學(xué)習(xí)的規(guī)則很少。這些規(guī)則一致且可預(yù)測(cè)地結(jié)合在一起。由于這些規(guī)則听皿,Kotlin 可以提供有用的、用戶可擴(kuò)展的語(yǔ)言特性——空安全宽档、多態(tài)性和無(wú)法訪問(wèn)的代碼分析——而無(wú)需在編譯器和 IDE 中求助于特殊情況和臨時(shí)檢查尉姨。

從頂部開(kāi)始

所有類型的 Kotlin 對(duì)象都被組織成子類型/超類型關(guān)系的層次結(jié)構(gòu)。

在該層次結(jié)構(gòu)的“頂部”是抽象類Any吗冤。例如又厉,類型 String 和 Int 都是Any.

image.png

Any相當(dāng)于Java的Object類。與 Java 不同欣孤,Kotlin 沒(méi)有區(qū)分語(yǔ)言固有的“原始”類型和用戶定義的類型馋没。它們都是同一類型層次結(jié)構(gòu)的一部分。

如果您定義的類不是從另一個(gè)類顯式派生的降传,則該類將是 Any 的直接子類型。

class Fruit(val ripeness: Double)
image.png

如果確實(shí)為用戶定義的類指定了基類勾怒,則基類將是新類的直接超類型婆排,但該類的最終祖先將是 Any 類型。

abstract class Fruit(val ripeness: Double)
class Banana(ripeness: Double, val bendiness: Double): 
    Fruit(ripeness)
class Peach(ripeness: Double, val fuzziness: Double): 
    Fruit(ripeness)
image.png

如果您的類實(shí)現(xiàn)了一個(gè)或多個(gè)接口笔链,它將具有多個(gè)直接超類型段只,其中 Any 作為最終祖先。

interface ICanGoInASalad
interface ICanBeSunDried

class Tomato(ripeness: Double): 
    Fruit(ripeness), 
    ICanGoInASalad, 
    ICanBeSunDried
image.png

Kotlin 類型檢查器強(qiáng)制執(zhí)行子類型/超類型關(guān)系鉴扫。

例如赞枕,您可以將子類型存儲(chǔ)到超類型變量中:

var f: Fruit = Banana(bendiness=0.5)
f = Peach(fuzziness=0.8)

但是您不能將超類型值存儲(chǔ)到子類型變量中:

val b = Banana(bendiness=0.5)
val f: Fruit = b
val b2: Banana = f
// Error: Type mismatch: inferred type is Fruit but Banana was expected

可空類型

與 Java 不同,Kotlin 區(qū)分“非空”和“可空”類型坪创。到目前為止我們看到的類型都是“非空”的炕婶。Kotlin 不允許null用作這些類型的值。您可以保證取消引用對(duì)“非空”類型值的引用永遠(yuǎn)不會(huì)拋出 NullPointerException莱预。

類型檢查器拒絕嘗試使用 null 或期望非 null 類型的可為 null 類型的代碼柠掂。

例如:

var s : String = null
// Error: Null can not be a value of a non-null type String

如果您希望某個(gè)值可能為空,則需要使用該值類型的可空等價(jià)物依沮,由后綴 '?' 表示涯贞。例如,該類型String?是可空等價(jià)的String危喉,因此允許所有字符串值加上空值宋渔。

var s : String? = null
s = "foo"
s = null
s = bar

類型檢查器確保您永遠(yuǎn)不會(huì)在沒(méi)有首先測(cè)試它不為空的情況下使用可空值。Kotlin 提供了操作符來(lái)使處理可為空類型更加方便辜限。有關(guān)示例皇拣,請(qǐng)參閱Kotlin 語(yǔ)言參考的Null Safety 部分

當(dāng)非空類型通過(guò)子類型關(guān)聯(lián)時(shí)列粪,它們的可空等價(jià)物也以相同的方式關(guān)聯(lián)审磁。例如谈飒,因?yàn)镾tring是 的子類型Any,String?是 的子類型Any?态蒂,因?yàn)锽anana是 的子類型Fruit杭措,Banana?是 的子類型Fruit?。

正如Any非空類型層次結(jié)構(gòu)Any?的根一樣钾恢, 是可為空類型層次結(jié)構(gòu)的根手素。因?yàn)锳ny?是Any 的超類型,Any?是 Kotlin 類型層次結(jié)構(gòu)的最頂層瘩蚪。

image.png

非空類型是其可空等價(jià)物的子類型泉懦。例如,String作為Any的子類型疹瘦,也是String?的子類型崩哩。

image.png

這就是為什么您可以將非空字符串值存儲(chǔ)到可為空字符串中的原因,但非空字符串變量不能存儲(chǔ)可為空的字符串言沐。Kotlin 的空安全性不是由特殊規(guī)則強(qiáng)制執(zhí)行的邓嘹,而是適用于非空類型之間的相同子類型/超類型規(guī)則的結(jié)果。

這也適用于用戶定義的類型層次結(jié)構(gòu)险胰。

image.png

Unit

Kotlin 是一種面向表達(dá)式的語(yǔ)言汹押。所有控制流語(yǔ)句(除了變量賦值,異常情況下)都是表達(dá)式起便。Kotlin 沒(méi)有像 Java 和 C 那樣的 void 函數(shù)棚贾。函數(shù)總是返回一個(gè)值。實(shí)際上不計(jì)算任何東西的函數(shù)——例如榆综,因?yàn)樗鼈兊母弊饔枚徽{(diào)用—— return Unit妙痹,一種具有單個(gè)值的類型,也稱為Unit.

大多數(shù)情況下奖年,您不需要顯式指定 Unit 作為返回類型或從函數(shù)返回 Unit细诸。如果你寫了一個(gè)帶有塊體的函數(shù)并且沒(méi)有指定結(jié)果類型,編譯器會(huì)把它當(dāng)作一個(gè)單元函數(shù)陋守。如果編寫單表達(dá)式函數(shù)震贵,編譯器可以推斷 Unit 返回類型,就像任何其他類型一樣水评。

fun example1() {
    println("block body and no explicit return type, so returns Unit")
}

val u1: Unit = example1()

fun example2() =
    println("single-expression function for which the compiler infers the return type as Unit")

val u2: Unit = example2()

沒(méi)什么特別的Unit猩系。像任何其他類型一樣,它是Any中燥, 它可以為空寇甸,因此是Unit?的子類型,它是Any?的子類型。

image.png

類型Unit?是一個(gè)奇怪的小邊緣情況拿霉,是 Kotlin 類型系統(tǒng)一致性的結(jié)果吟秩。它只有兩個(gè)成員:Unit值和null。我從來(lái)沒(méi)有發(fā)現(xiàn)需要明確地使用它绽淘,但是類型系統(tǒng)中沒(méi)有“void”的特殊情況這一事實(shí)使得通用地處理所有類型的函數(shù)變得更加容易涵防。

Nothing

在 Kotlin 類型層次結(jié)構(gòu)的最底層是 Nothing類型。

image.png

顧名思義沪铭,Nothing 是一種沒(méi)有實(shí)例的類型壮池。類型為 Nothing 的表達(dá)式不會(huì)產(chǎn)生值。

請(qǐng)注意 Unit 和 Nothing 之間的區(qū)別杀怠。表達(dá)式類型 Unit 的計(jì)算結(jié)果為單例值Unit椰憋。對(duì)類型為 Nothing 的表達(dá)式的求值根本不會(huì)返回。

這意味著任何跟在 Nothing 類型表達(dá)式后面的代碼都是不可訪問(wèn)的赔退。編譯器和 IDE 會(huì)警告您此類無(wú)法訪問(wèn)的代碼橙依。

什么樣的表達(dá)式計(jì)算為Nothing?那些執(zhí)行控制流的离钝。

例如票编,throw關(guān)鍵字中斷表達(dá)式的計(jì)算并從封閉函數(shù)中拋出異常。因此卵渴,throw 是 Nothing 類型的表達(dá)式。

通過(guò)將 Nothing 作為所有其他類型的子類型鲤竹,類型系統(tǒng)允許程序中的任何表達(dá)式實(shí)際上無(wú)法計(jì)算值浪读。這模擬了現(xiàn)實(shí)世界的可能性,例如 JVM 在計(jì)算表達(dá)式時(shí)內(nèi)存不足辛藻,或者有人拔掉了計(jì)算機(jī)的電源插頭碘橘。這也意味著我們可以從任何表達(dá)式中拋出異常。

fun formatCell(value: Double): String =
    if (value.isNaN()) 
        throw IllegalArgumentException("$value is not a number") 
    else 
        value.toString()

得知該return語(yǔ)句的類型為 Nothing 時(shí)可能會(huì)感到驚訝吱肌。Return 是一個(gè)控制流語(yǔ)句痘拆,它立即從封閉函數(shù)返回一個(gè)值,中斷對(duì)它所屬的任何表達(dá)式的求值氮墨。

fun formatCellRounded(value: Double): String =
    val rounded: Long = if (value.isNaN()) return "#ERROR" else Math.round(value)
    rounded.toString()

進(jìn)入無(wú)限循環(huán)或終止當(dāng)前進(jìn)程的函數(shù)的結(jié)果類型為 Nothing纺蛆。例如,Kotlin 標(biāo)準(zhǔn)庫(kù)將exitProcess函數(shù)聲明為:

fun exitProcess(status: Int): Nothing

如果您編寫自己的返回 Nothing 的函數(shù)规揪,編譯器將在調(diào)用您的函數(shù)后檢查無(wú)法訪問(wèn)的代碼桥氏,就像使用內(nèi)置控制流語(yǔ)句一樣。

inline fun forever(action: ()->Unit): Nothing {
    while(true) action()
}

fun example() {
    forever {
        println("doing...")
    }
    println("done") // Warning: Unreachable code
}

與空安全一樣猛铅,無(wú)法訪問(wèn)的代碼分析不是通過(guò) IDE 和編譯器中的臨時(shí)字支、特殊情況檢查來(lái)實(shí)現(xiàn)的,因?yàn)樗仨氃?Java 中進(jìn)行。這是類型系統(tǒng)的一個(gè)函數(shù)堕伪。

什么都可以為空揖庄?

Nothing,與任何其他類型一樣欠雌,可以設(shè)為可為空蹄梢,并給出類型Nothing?。 Nothing?可以只包含一個(gè)值:null桨昙。事實(shí)上检号,Nothing? 是null的類型。

Nothing?是所有可空類型的最終子類型蛙酪,它允許將該值null用作任何可空類型的值齐苛。

image.png

結(jié)論

當(dāng)您一次性考慮所有這些時(shí),Kotlin 的整個(gè)類型層次結(jié)構(gòu)可能會(huì)感覺(jué)非常復(fù)雜桂塞。

image.png

但永遠(yuǎn)不要害怕凹蜂!

我希望這篇文章已經(jīng)證明 Kotlin 有一個(gè)簡(jiǎn)單且一致的類型系統(tǒng)。需要學(xué)習(xí)的規(guī)則很少:Any?頂部和Nothing底部的超類型/子類型關(guān)系的層次結(jié)構(gòu)阁危,以及非空類型和可空類型之間的子類型關(guān)系玛痊。就是這樣。沒(méi)有特殊情況狂打±奚罚空安全、面向?qū)ο蟮亩鄳B(tài)性和無(wú)法訪問(wèn)的代碼分析等有用的語(yǔ)言功能都源于這些簡(jiǎn)單趴乡、可預(yù)測(cè)的規(guī)則对省。由于這種一致性,Kotlin 的類型檢查器是一個(gè)強(qiáng)大的工具晾捏,可以幫助您編寫簡(jiǎn)潔蒿涎、正確的程序。

英文好的同學(xué)可以直接閱讀原文

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惦辛,一起剝皮案震驚了整個(gè)濱河市劳秋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胖齐,老刑警劉巖玻淑,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異市怎,居然都是意外死亡岁忘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門区匠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)干像,“玉大人帅腌,你說(shuō)我怎么就攤上這事÷樘” “怎么了速客?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)五鲫。 經(jīng)常有香客問(wèn)我溺职,道長(zhǎng),這世上最難降的妖魔是什么位喂? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任浪耘,我火速辦了婚禮,結(jié)果婚禮上塑崖,老公的妹妹穿的比我還像新娘七冲。我一直安慰自己,他們只是感情好规婆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布澜躺。 她就那樣靜靜地躺著,像睡著了一般抒蚜。 火紅的嫁衣襯著肌膚如雪掘鄙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天嗡髓,我揣著相機(jī)與錄音操漠,去河邊找鬼。 笑死饿这,一個(gè)胖子當(dāng)著我的面吹牛颅夺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛹稍,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼部服!你這毒婦竟也來(lái)了唆姐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤廓八,失蹤者是張志新(化名)和其女友劉穎奉芦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體剧蹂,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡声功,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宠叼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片先巴。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡其爵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伸蚯,到底是詐尸還是另有隱情摩渺,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布剂邮,位于F島的核電站摇幻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挥萌。R本人自食惡果不足惜绰姻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望引瀑。 院中可真熱鬧狂芋,春花似錦、人聲如沸伤疙。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)徒像。三九已至黍特,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锯蛀,已是汗流浹背灭衷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旁涤,地道東北人翔曲。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劈愚,于是被迫代替她去往敵國(guó)和親瞳遍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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