子類型
多態 |
---|
特設多態 |
參數多態 |
子類型 |
在編程語言理論中,子類型(動名詞,英語:subtyping)是一種類型多態的形式。這種形式下,子類型(名詞,英語:subtype)可以替換另一種相關的數據類型(超類型,英語:supertype)。也就是說,針對超類型元素進行操作的子程序、函數等程序元素,也可以操作相應的子類型。如果 S 是 T 的子類型,這種子類型關係通常寫作 S <: T,意思是在任何需要使用 T 類型對象的環境中,都可以安全地使用 S 類型的對象。子類型的準確語義取決於具體的編程語言中「X 環境中,可以安全地使用 Y」的意義。編程語言的類型系統定義了各自不同的子類型關係。
由於子類型關係的存在,某個對象可能同時屬於多種類型,因此,子類型(英語:subtyping)是一種類型多態的形式,也被稱作子類型多態(英語:subtype polymorphism)或者包含多態(英語:inclusion polymorphism)。在面向對象程序設計中,多態一般僅指這裡所說的「子類型多態」,而「參數多態」則一般被稱作泛型編程。
子類型與面向對象語言中(類或對象)的繼承是兩個概念。子類型反映了類型(即面向對象中的接口)之間的關係;而繼承反映了一類對象可以從另一類對象創造出來,是語言特性的實現。因此,子類型也稱接口繼承;繼承稱作實現繼承。
例子
右圖中給出了子類型的一個簡單實際例子。一般性對象「鳥」(或超類型)引發了三個派生對象(或子類型)「鴨子」、「杜鵑」和「鴕鳥」。每個都以自己的方式改變了基本的「鳥」的概念,但仍繼承了很多「鳥」的特徵。一個數據對象可以被聲名為這四種類型中任何一個。這個圖中使用了 UML 符號,箭頭指示方向和超類型和它的子類型之間的聯繫。
在多數基於類的面向對象編程語言中,子類引出子類型:如果 A 是 B 的子類,則類 A 的實例可以用在期望類 B 的實例的任何上下文中;所以我們稱 A 是 B 的子類型。一個結論就是聲明有類型 B 的任何變量或形式參數在運行時間可以持有類 A 的一個值;在這種情況下很多面向對象編程者會聲稱 B 是這個變量的「靜態類型」而 A 是它的「動態類型」。這個規則的例外包括 C++語言中的私有繼承(它不建立子類型),和 Eiffel 語言中在派生類型上特定運算,在其中繼承自基類的特徵可以用違反子類型規則的方式去除或修改。
另一個例子是可以允許整數值被用在期望浮點數值的地方,或可以定義包含整數和實數二者的一個類型 number 的語言。在第一種情況下,整數類型將是浮點數類型的子類型;在第二種情況下,這兩個類型都是 number 的子類型而相互之間無子類型關係。
編程者可利用子類型來以比沒有它更抽象的方式來寫代碼。考慮下面的例子:
function max (x as number, y as number) is if x < y then return y else return x end
如果整數和實數都是 number
的子類型,則二者任何類型都可以傳遞給這個函數。為此,子類型經常被認為是一種形式的多態性。上述例子也可以比較於 C++ 語言的模板。
在類型論中,子類型關係經常寫為 <:,有着 A<:B 意味着 A 是 B 的子類型。在類型論中子類型可用如下事實來特徵化,如果 A<:B,類型 A 的任何表達式也可被給予類型 B;立法這個特徵化的形式類型規則叫做「包容」規則。
子類型方案
類型理論研究者區分兩類類型系統:
- 名義子類型,在其中只有類型聲明的名字相同才算是相同類型,子類型關係必須被顯式聲明。C, C++, C#, Java, Objective-C等語言均屬於這類。
- 結構子類型,在其中兩種類型的結構組成決定了一種類型是否是另一種類型的子類型。
上面描述的基於類的面向對象子類型描述是名義的;面向對象的結構子類型規則可以聲稱,如果類型 A 的一個對象能處理類型 B 的對象能處理的所有消息(就是說,如果它們定義都同樣的方法),則 A 是 B 的子類型,不管二者任何一個是否從繼承自其他對象。不是對象類型的類型的健壯的結構子類型規則也是周知的。
帶有子類型的編程語言實現可分為兩大類:
- 如果 A<:B,類型 A 的任何值的表示也表示類型 B 的相同值,則為「包含實現」(inclusive implementation)。
- 類型 A 的值可自動的轉換成類型 B 的值,則為「強制實現」( coercive implementation)。即類型強制轉換之意。
在面向對象語言中子類型所導致的子類型通常是包含的;聯繫整數和浮點數的子類型關係,它們有不同表示,通常是強制的。
在定義子類型關係的幾乎所有類型系統中,它是自反的(意味着對於任何類型 A 有 A<:A)和傳遞的(意味着如果 A<:B 並且 B<:C 則 A<:C)。這得到了在類型上的預序。
記錄類型
記錄是命名的域(field)的集合。記錄類型(types of records)的子類型化包括寬度與深度兩種方式。
- 寬度子類型化(width subtyping):給記錄增加更多的域。
- 深度子類型化(depth subtyping):把超類型(supertype)的域替換為域的子類型。這僅能用於只讀(immutable)記錄。
函數類型
對於函數類型T1 → T2,其子類型為S1 → S2,則T1 <: S1且S2 <: T2。參數類型S1 → S2為逆變,返回類型為協變。
允許副作用的語言,如大部分面向對象語言,子類型化還不足以保證安全在另一個上下文中使用。行為子類型化要求保持不變。[1]
可變引用(mutable reference)的子類型化類似於函數參數與返回值的處理。只寫引用是逆變的;只讀引用是協變的;可變引用是不變的。
類型強制
在強制子類型化系統(coercive subtyping system),子類型通過從子類型到超類型的隱式類型轉換函數得以定義。對於每個子類型關係 (S <: T),一個強制關係coerce: S → T,使得任何對象s 為類型S,可以視作對象coerceS → T(s)具有類型T。類型強制函數可以複合:如果S <: T且T <: U,難麼s 可以看作類型u在複合強制關係(coerceT → U ∘ coerceS → T)。類型到其自身的coerceT → T是同一函數idT
參考文獻
引用
- ^ Barbara Liskov, Jeannette Wing, A behavioral notion of subtyping (頁面存檔備份,存於網際網路檔案館), ACM Transactions on Programming Languages and Systems (TOPLAS), Volume 16, Issue 6 (November 1994), pp. 1811 - 1841. An updated version appeared as CMU technical report: Liskov, Barbara; Wing, Jeannette. Behavioral Subtyping Using Invariants and Constraints (PS). July 1999 [2006-10-05]. (原始內容存檔於2012-08-30).
來源
- Object-Oriented Software Construction by Bertrand Meyer
外部連結
參見