Kotlin 的類型層次結(jié)構(gòu)需要學(xué)習(xí)的規(guī)則很少。這些規(guī)則一致且可預(yù)測地結(jié)合在一起。由于這些規(guī)則,Kotlin 可以提供有用的、用戶可擴(kuò)展的語言特性——空安全、多態(tài)性和無法訪問的代碼分析——而無需在編譯器和 IDE 中求助于特殊情況和臨時(shí)檢查。
從頂部開始
所有類型的 Kotlin 對象都被組織成子類型/超類型關(guān)系的層次結(jié)構(gòu)。
在該層次結(jié)構(gòu)的“頂部”是抽象類Any。例如,類型 String 和 Int 都是Any.

Any相當(dāng)于Java的Object類。與 Java 不同,Kotlin 沒有區(qū)分語言固有的“原始”類型和用戶定義的類型。它們都是同一類型層次結(jié)構(gòu)的一部分。
如果您定義的類不是從另一個(gè)類顯式派生的,則該類將是 Any 的直接子類型。
class Fruit(val ripeness: Double)

如果確實(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)

如果您的類實(shí)現(xiàn)了一個(gè)或多個(gè)接口,它將具有多個(gè)直接超類型,其中 Any 作為最終祖先。
interface ICanGoInASalad
interface ICanBeSunDried
class Tomato(ripeness: Double):
Fruit(ripeness),
ICanGoInASalad,
ICanBeSunDried

Kotlin 類型檢查器強(qiáng)制執(zhí)行子類型/超類型關(guān)系。
例如,您可以將子類型存儲到超類型變量中:
var f: Fruit = Banana(bendiness=0.5)
f = Peach(fuzziness=0.8)
但是您不能將超類型值存儲到子類型變量中:
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用作這些類型的值。您可以保證取消引用對“非空”類型值的引用永遠(yuǎn)不會拋出 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)不會在沒有首先測試它不為空的情況下使用可空值。Kotlin 提供了操作符來使處理可為空類型更加方便。有關(guān)示例,請參閱Kotlin 語言參考的Null Safety 部分。
當(dāng)非空類型通過子類型關(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)的最頂層。

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

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

Unit
Kotlin 是一種面向表達(dá)式的語言。所有控制流語句(除了變量賦值,異常情況下)都是表達(dá)式。Kotlin 沒有像 Java 和 C 那樣的 void 函數(shù)。函數(shù)總是返回一個(gè)值。實(shí)際上不計(jì)算任何東西的函數(shù)——例如,因?yàn)樗鼈兊母弊饔枚徽{(diào)用—— return Unit,一種具有單個(gè)值的類型,也稱為Unit.
大多數(shù)情況下,您不需要顯式指定 Unit 作為返回類型或從函數(shù)返回 Unit。如果你寫了一個(gè)帶有塊體的函數(shù)并且沒有指定結(jié)果類型,編譯器會把它當(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()
沒什么特別的Unit。像任何其他類型一樣,它是Any, 它可以為空,因此是Unit?的子類型,它是Any?的子類型。

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

顧名思義,Nothing 是一種沒有實(shí)例的類型。類型為 Nothing 的表達(dá)式不會產(chǎn)生值。
請注意 Unit 和 Nothing 之間的區(qū)別。表達(dá)式類型 Unit 的計(jì)算結(jié)果為單例值Unit。對類型為 Nothing 的表達(dá)式的求值根本不會返回。
這意味著任何跟在 Nothing 類型表達(dá)式后面的代碼都是不可訪問的。編譯器和 IDE 會警告您此類無法訪問的代碼。
什么樣的表達(dá)式計(jì)算為Nothing?那些執(zhí)行控制流的。
例如,throw關(guān)鍵字中斷表達(dá)式的計(jì)算并從封閉函數(shù)中拋出異常。因此,throw 是 Nothing 類型的表達(dá)式。
通過將 Nothing 作為所有其他類型的子類型,類型系統(tǒng)允許程序中的任何表達(dá)式實(shí)際上無法計(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語句的類型為 Nothing 時(shí)可能會感到驚訝。Return 是一個(gè)控制流語句,它立即從封閉函數(shù)返回一個(gè)值,中斷對它所屬的任何表達(dá)式的求值。
fun formatCellRounded(value: Double): String =
val rounded: Long = if (value.isNaN()) return "#ERROR" else Math.round(value)
rounded.toString()
進(jìn)入無限循環(huán)或終止當(dāng)前進(jìn)程的函數(shù)的結(jié)果類型為 Nothing。例如,Kotlin 標(biāo)準(zhǔn)庫將exitProcess函數(shù)聲明為:
fun exitProcess(status: Int): Nothing
如果您編寫自己的返回 Nothing 的函數(shù),編譯器將在調(diào)用您的函數(shù)后檢查無法訪問的代碼,就像使用內(nèi)置控制流語句一樣。
inline fun forever(action: ()->Unit): Nothing {
while(true) action()
}
fun example() {
forever {
println("doing...")
}
println("done") // Warning: Unreachable code
}
與空安全一樣,無法訪問的代碼分析不是通過 IDE 和編譯器中的臨時(shí)、特殊情況檢查來實(shí)現(xiàn)的,因?yàn)樗仨氃?Java 中進(jìn)行。這是類型系統(tǒng)的一個(gè)函數(shù)。
什么都可以為空?
Nothing,與任何其他類型一樣,可以設(shè)為可為空,并給出類型Nothing?。 Nothing?可以只包含一個(gè)值:null。事實(shí)上,Nothing? 是null的類型。
Nothing?是所有可空類型的最終子類型,它允許將該值null用作任何可空類型的值。

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

但永遠(yuǎn)不要害怕!
我希望這篇文章已經(jīng)證明 Kotlin 有一個(gè)簡單且一致的類型系統(tǒng)。需要學(xué)習(xí)的規(guī)則很少:Any?頂部和Nothing底部的超類型/子類型關(guān)系的層次結(jié)構(gòu),以及非空類型和可空類型之間的子類型關(guān)系。就是這樣。沒有特殊情況??瞻踩?、面向?qū)ο蟮亩鄳B(tài)性和無法訪問的代碼分析等有用的語言功能都源于這些簡單、可預(yù)測的規(guī)則。由于這種一致性,Kotlin 的類型檢查器是一個(gè)強(qiáng)大的工具,可以幫助您編寫簡潔、正確的程序。
英文好的同學(xué)可以直接閱讀原文