本文由 Prefert 發表在 ScalaCool 團隊部落格。
我們在Dive Into Kotlin(二):Kotlin 型別結構設計中已經對Kotlin的型別系統進行過大致的介紹。
文中提到過: Any
型別是 Kotlin 中 所有非空型別(ex: String
, Int
) 的根型別。
當我們需要和 Java 互操作的時候,Kotlin 把 Java 方法引數和返回型別中用到的 Object
型別看作 Any
(更確切地說是當做「平臺型別」)。當 Kotlin 函式中使用 Any
時,它會被編譯成 Java 位元組碼中的 Object
。
什麼是平臺型別?
平臺型別本質上就是 Kotlin 不知道可空性資訊的型別—所有 Java 引用型別在 Kotlin 中都表現為平臺型別。當在 Kotlin 中處理平臺型別的值的時候,它既可以被當做可空型別來處理,也可以被當做非空型別來操作。
平臺型別的引入是 Kotlin 相容 Java 時的一種權衡設計。試想下,如果所有來自 Java 的值都被看成非空,那麼就容易寫出比較危險的程式碼。反之,如果 Java 值都強制當做可空,則會導致大量的
null
檢查。綜合考量,平臺型別是一種折中的設計方案。
在 Java 中,Object
型別位於其型別系統的頂級。如果說 Kotlin 與 Java 是100%相容的,那我們是否可以說 Any 也是Kotlin的所有型別的頂級型別呢?在上一篇文章中,這個問題同樣困擾了我,官方也並沒有做出一個明確的說明。但是我們可以注意到的是, Kotlin 中引入了「可空型別」這個概念,這很可能會對系統層級結構產生影響。
在探索根型別之前,先讓我們理清兩個概念:繼承(Inheriting
)和 子型別化(Subtyping
)。
繼承和子型別化的區別
這是一個看似容易實則不簡單的問題:到底什麼才是子型別化( Subtyping
)?我們曾在Subtyping vs Typeclasses(一)這篇部落格討論過這個問題。如果你只有 Java 這門程式語言的開發經驗,很容易陷入一個誤區——繼承關係決定父子型別關係。因為在 Java 中, 類與型別大部分情況下都是「等價」的(在 Java 泛型出現前)。
事實上,「繼承」和「子型別化」是兩個完全不同的概念。
-
子型別化的核心是一種型別的替代關係(我們也可以稱之為子型別多型),通常可表示為:
S <: T 複製程式碼
以上
S
是T
的子類,這意味著在需要T
型別值的地方,S
型別的值同樣適用,如在 Kotlin 中Int
是Number
的子類:fun printNum(num: Number) { println(num) } >>> val n: Int = 1 >>> printNum(n) >>> 1 >>> printNum("I am a String") error: type mismatch: inferred type is String but Number was expected 複製程式碼
-
相對而言,繼承強調的是一種「實現上的複用」,而子型別化是一種型別語義的關係,與實現沒關係。在 Java 中,我們似乎也可以通過類繼承來實現上述關係:
class S extends class T 複製程式碼
由於在宣告父子型別關係的同時,也宣告瞭繼承的關係,所以通常會造成了某種程度上的混淆,但是這並不能說明這兩個概念就是等價的。
雖然 Any
與 Any?
看起來沒有繼承關係,然而當我們在需要用 Any?
型別值的地方,顯然可以傳入一個型別為 Any
的值,這在編譯上不會產生問題。反之卻行不通,比如:一個引數型別為 Any
的函式,我們傳入符合 Any?
型別的 null
值,就會出現如下的錯誤:
error: null can not be a value of a non-null type Any
複製程式碼
以上,我們也可以初步得出結論:Any
的值能在所有情況下代替 Any?
的值,這符合「子型別化」的概念。
因此,我們可以很大膽地說:在Kotlin的型別系統中,Any?
是 Any
的父型別,而且是所有型別的根型別,雖然當前的 Kotlin 官網文件沒有介紹過這一點。
Any? 與 Any??
一個你可能會挑戰的問題是,如果 Any?
是 Any
的父型別,那麼 Any??
是否又是 Any?
的父型別, 如果成立,那麼是否意味著就沒有所謂的「所有型別的根型別」了?
其實,Kotlin 中的可空型別可以看做所謂的 Union Type
,在程式中通常用 A | B
表示,近似於數學中的並集。如果用型別的並集來表示 Any?
,可寫為 Any ∪ Null
。相應的 Any??
就表示為 Any ∪ Null ∪ Null
,這等價於 Any ∪ Null
, 即 Any??
等價於 Any?
。因此,說 Any?
是所有型別的根型別是沒有問題的。