複製鏈接
請複製以下鏈接發送給好友

子類型

鎖定
編程語言理論中,子類型是一種類型多態的形式。這種形式下,子類型可以替換另一種相關的數據類型(超類型,英語:supertype)。也就是説,針對超類型元素進行操作的子程序、函數等程序元素,也可以操作相應的子類型。如果 S 是 T 的子類型,這種子類型關係通常寫作 S <: T,意思是在任何需要使用 T 類型對象的環境中,都可以安全地使用 S 類型的對象。子類型的準確語義取決於具體的編程語言中“X 環境中,可以安全地使用 Y”的意義。編程語言的類型系統定義了各自不同的子類型關係。
子類型是c++繼承機制的產物:以public方式繼承的派生類可看成基類的子類型。 [1] 
中文名
子類型
外文名
subtype
適用範圍
c++

子類型定義

由於子類型關係的存在,某個對象可能同時屬於多種類型,因此,子類型是一種類型多態的形式,也被稱作子類型多態(subtype polymorphism)或者包含多態(inclusion polymorphism)。在面向對象程序設計中,多態一般僅指這裏所説的“子類型多態”,而“參數多態”則一般被稱作泛型程序設計。
子類型與面嚮對象語言中(類或對象)的繼承是兩個概念。子類型反映了類型(即面向對象中的接口)之間的關係;而繼承反映了一類對象可以從另一類對象創造出來,是語言特性的實現。因此,子類型也稱接口繼承;繼承稱作實現繼承。
如果一個類型S是另一個類型T的子類型,則對用T表達式的所有程序P,當用S替換程序P中的T時,程序P的功能不變 [1] 
在c++中,以public方式繼承的派生類可看做基類的子類型。

子類型性質

某種程度上來説,類型S與類型T等價。類型S包含T的全部內容,但是類型T不一定包含S的全部內容。對基類所能實施的操作完全適用於派生類(子類型),一個子類型的數據也可以賦值或作為參數傳遞給類型T的變量。具體可總結為一下幾點:
·基類的操作可以實施到派生類對象
·基類指針變量可以指向派生類對象
·派生類對象可以賦值給基類對象
·派生類操作不可以用於基類對象
·派生類指針變量不可以指向基類對象
·基類對象不能賦值給派生類對象

子類型舉例

圖2 圖2
圖2中給出了子類型的一個簡單實際例子。一般性對象“鳥”(或超類型)引發了三個派生對象(或子類型)“鴨子”、“杜鵑”和“鴕鳥”。每個都以自己的方式改變了基本的“鳥”的概念,但仍繼承了很多“鳥”的特徵。一個數據對象可以被聲名為這四種類型中任何一個。這個圖中使用了UML符號,箭頭指示方向和超類型和它的子類型之間的聯繫。
在多數基於類的面向對象編程語言中,子類引出子類型:如果AB的子類,則類A的實例可以用在期望類B的實例的任何上下文中;所以我們稱AB的子類型。一個結論就是聲明有類型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意味着AB的子類型。在類型論中子類型可用如下事實來特徵化,如果A<:B,類型A的任何表達式也可被給予類型B;立法這個特徵化的形式類型規則叫做“包容”規則。

子類型方案

類型理論研究者區分兩類類型系統:
  • 名義子類型,在其中只有類型聲明的名字相同才算是相同類型,子類型關係必須被顯式聲明。CC++C#JavaObjective-C等語言均屬於這類。
  • 結構子類型,在其中兩種類型的結構組成決定了一種類型是否是另一種類型的子類型。
上面描述的基於類的面向對象子類型描述是名義的;面向對象的結構子類型規則可以聲稱,如果類型A的一個對象能處理類型B的對象能處理的所有消息(就是説,如果它們定義都同樣的方法),則AB的子類型,不管二者任何一個是否從繼承自其他對象。不是對象類型的類型的健壯的結構子類型規則也是周知的。
帶有子類型的編程語言實現可分為兩大類:
  • 如果A<:B,類型A的任何值的表示也表示類型B的相同值,則為“包含實現”(inclusive implementation)。
  • 類型A的值可自動的轉換成類型B的值,則為“強制實現”( coercive implementation)。即類型強制轉換之意。
在面嚮對象語言中子類型所導致的子類型通常是包含的;聯繫整數和浮點數的子類型關係,它們有不同表示,通常是強制的。
在定義子類型關係的幾乎所有類型系統中,它是自反的(意味着對於任何類型AA<:A)和傳遞的(意味着如果A<:B並且B<:CA<:C)。這得到了在類型上的預序。

子類型記錄類型

記錄是命名的域(field)的集合。記錄類型(types of records)的子類型化包括寬度與深度兩種方式。
  • 寬度子類型化(width subtyping):給記錄增加更多的域。
  • 深度子類型化(depth subtyping):把超類型(supertype)的域替換為域的子類型。這僅能用於只讀(immutable)記錄。

子類型函數類型

對於函數類型T1→ T2,其子類型為S1→ S2,則T1<: S1且S2<: T2。參數類型S1→ S2為逆變,返回類型為協變
允許副作用的語言,如大部分面嚮對象語言,子類型化還不足以保證安全在另一個上下文中使用。行為子類型化要求保持不變
可變引用(mutable reference)的子類型化類似於函數參數與返回值的處理。只寫引用是逆變的;只讀引用是協變的;可變引用是不變的。 [2] 

子類型類型強制

在強制子類型化系統(coercive subtyping system),子類型通過從子類型到超類型的隱式類型轉換函數得以定義。對於每個子類型關係 (S<:T),一個強制關係coerce:ST,使得任何對象s為類型S,可以視作對象coerceST(s)具有類型T。類型強制函數可以複合:如果S<:TT<:U,難麼s可以看作類型u在複合強制關係(coerceTUcoerceST)。類型到其自身的coerceTT是同一函數idT
參考資料
  • 1.    陳家俊 鄭滔.程序設計教程 ——用C++語言編程.南京大學:機械工業出版社,2015年:270-271
  • 2.    Liskov B H, Wing J M. A behavioral notion of subtyping[M]. ACM, 1994.