Swift 程式語言 — Inheritance
讓我們來探討一下 Swift 中所謂的繼承。
# 前言
Classes 可以繼承方法、屬性和其他特性。當一個 class 繼承另一個 class 時,繼承的 class 稱之為 subClass,被繼承的 class 稱為 superClass。繼承是一個基本的行為,他將 class 與 Swift 中的其他類型區隔開。
Swift 中的 Classes 可以調用和訪問屬於 subClass 的方法、屬性和下標,並且能夠提供這些方法、屬性、下標的自己的重寫版本(override),來優化或修改這些行為。Swift 透過檢查 override 定義是否具有匹配的 superClass 定義來幫助確認你的 override 是正確的。
Classes 還可以添加屬性觀察器到繼承的屬性,以便於在屬性值更改時得到通知。屬性觀察器可以添加到任何屬性,無論它最初是被定義為存儲屬性還是計算屬性。
# 定義 Base Class
任何不從其他 class 繼承的 class 都稱之為 base class。
Swift 類不會從一個通用 baseClass 繼承。你沒有指定特定 superClass 的 class 都會以 baseClass 的形式創建。
下方的範例定義了一個名為 Vehicle
的 baseClass。這個 class
定義了名為 currentSpeed
的存儲屬性,默認值為 0.0
。currentSpeed
屬性的值由名為 description
的唯讀計算屬性使用,用來創建車輛的描述。
Vehicle
還定義了一個名為 makeNoise
的方法。此方法實際上不對 Vehicle
實例進行任何操作,但稍後將會由 Vehicle
的 subClass 自定義:
使用初始化器語法來創建 Vehicle 實例,該語法寫為類型名稱()
:
let someVehicle = Vehicle()
在創建 Vehicle
新實例時,你可以訪問其訪問描述屬性來印出車輛當前速度的可讀描述:
Vehicle
定義了任意車輛的共同特徵,但其本身並沒有太多的用途。為了使其更有用,你需要對其進行優化來描述更具體的車輛類型。
# Subclassing
Subclassing 是在現有 class 上創建新的 class 的行為。subClass 繼承現有 class 的特徵,然後可以對其進行重新定義。你還可以向 subClass 添加新特徵:
要表示 subClass 具有 superClass,請在 subClass 之後編寫 superClass 名稱,使用冒號 (:)
分隔:
class SomeSubclass: SomeSuperclass {
// 在這邊定義 subClass
}
下面我們定義了名為 Bicycle
的 subClass,包含一個 Vehicle
的 superClass:
新的 Bicycle
class 自動獲取 Vehicle
的所有特性,例如 currentSpeed
和 description
屬性和 makeNoise
方法。
除了它繼承的特性以外,Bicycle
還定義了一個新的存儲屬性 hasBasket
,其默認值為 false
。
默認情況下,你所創建的任何 Bicycle
實例都沒有籃子(hasBasket
默認為 false
)。在創建實例之後,你可以將特定 Bicycle
實例的 hasBasket
屬性設為 true
。
你也能夠修改 Bicycle
實例繼承的 currentSpeed
屬性,並查詢實例繼承的 description
屬性。
SubClasses 本身也可以被繼承,下面創建了一個雙人座的腳踏車的 subClass 稱為 tandem
:
Tandem
繼承了 Bicycle
中所有的屬性及方法,而 Bicycle
繼承了 Vehicle
的所有屬性及方法。Tandem
還添加了一個名為 currentNumberOfPassengers
的存儲屬性,默認為 0
。
如果創建 Tandem
實例,則可以使用其任何新增的和繼承的屬性,並且查詢它從 Vehicle
繼承的唯讀 description
屬性:
# Overriding
SubClass 能夠提供自己的實例方法、類型方法、實例屬性、類型屬性或是下標的自定義實現,否則他將從 superClass 中繼承,這稱之為 overriding
。
要覆蓋特性否則他將會是繼承的特性,請使用 override
關鍵字作為覆寫定義的的前綴。如此以來可以闡述你打算提供覆寫並且沒有錯誤的提供匹配的定義。意外的覆寫可能導致意外行為,並且在編輯程式碼時,沒有 override
關鍵字的任何覆寫都會被判斷為錯誤。
override
關鍵字還提示 Swift 編譯器檢查你所覆寫的 superClass(或是父類其中ㄧ)是否具有與之相匹配的宣告來供你覆寫。這個檢查可以確保你覆寫的定義是正確的。
# 訪問 SuperClass 方法、屬性及下標
當你提供 subClass 方法、屬性或是下標的覆寫時,將現有 superClass 實現作為覆寫的一部份有時很有用。例如,你可以優化現有實現的行為,或將修改後的值存儲在現有繼承的變數中。
如果這是合適的,你可以使用 super
前綴來訪問方法、屬性或下標的 superClass 版本
- 一個名為
someMethod()
的覆寫方法可以透過在覆寫方法實現中調用super.someMethod()
來調用 superClass 的someMethod()
。 - 一個名為
someProperty
的覆寫屬性可以透過在覆寫getter
或setter
中以super.someProperty
來訪問superClass
的someProperty
。 - 一個名為
someIndex
的覆寫下標可以使用super[someIndex]
在覆寫的下標實現中訪問superClass
中相同的下標。
# Overriding Methods
你可以覆寫繼承的實例或類型方法,來在 subClass 中提供定製或替代實現。
下面定義了一個名為 Train
的 Vehicle
新的 subClass,它覆寫了 Train
從 Vehicle
中繼承的 makeNoise()
方法:
如果你創建一個 Train
實例並且調用其 makeNoise()
方法,你可以看到該方法的 Train
的 subClass 版本被調用:
# Overriding Properties
你可以覆寫繼承的實例或類型屬性,來為該屬性提供自定義的 getter
或 setter
,或是添加屬性觀察器來使覆寫的屬性能夠在底層屬性值更改時進行觀察。
● Overriding Property Getters 和 Setters
你可以提供自定義的 getter
和 setter
(如果適用)來覆寫任何繼承的屬性,無論繼承的屬性是在來源中實現為存儲屬性還是計算屬性。SubClass 不知道繼承的屬性的存儲或計算性質,他只知道繼承的屬性具有特定的名稱和類型。你必須始終宣告要覆寫的屬性的名稱和類型,來使編譯器能夠檢查你的覆寫是否具有相同名稱和類型的 superClass 的屬性匹配。
透過在 subClass 屬性中覆寫提供 getter
和 setter
,可以將繼承的唯讀屬性作為讀寫屬性提供。但是,你無法將繼承的讀寫屬性顯示為唯讀屬性。
如果將 setter
作為屬性覆寫的一部份提供,則還必須要為該覆寫提供 getter
。如果你不想在覆寫 getter
中修改繼承屬性的值,則可以透過從 getter
中返回 super.someProperty
來簡單的傳遞繼承的值,其中的 someProperty
是你要覆寫的屬性名稱。
下面是犯了一個稱為 Car
的 class
,它為 Vehicle
的 subClass。Car
引入了一個名為 gear
的新存儲屬性,默認值為 1
。Car
還會覆寫它從 Vehicle
繼承的 description
屬性,來提供包含當前檔位的自定義描述:
覆寫的 description
屬性首先調用 super.description
,它返回 Vehicle
的 description
屬性。然後,Car
的描述版本在此描述的末尾添加了一些額外的訊息,來提供關於當前 gear
的訊息。
如果你創建 Car
的實例並且設置 gear
和 currentSpeed
屬性,則可以看到其 description
屬性返回 Car
中定義的定製描述:
● Overriding 屬性觀察器
你可以使用屬性覆寫來添加屬性觀察器到繼承的屬性。這使你能夠在繼承的屬性值更改時收到通知,無論該屬性最初如何實現。
你不能將屬性觀察器添加到繼承的常數存儲屬性或繼承的唯讀計算屬性。無法設置這些屬性的值,因此不適合將
willSet
或didSet
實現作為覆寫的一部份來提供。另外請注意,你不能為同一個屬性提供覆寫的
setter
和覆寫屬性觀察器。如果想要觀察屬性值得更改,並且已經為該屬性提供了自定義setter
,則只需要觀察自定義setter
中的任何值更改即可。
下面的範例定義了一個名為 AutomaticCar
的 class
,為 Car
的 subClass。Automatic
表示帶有自動變速器的汽車,它會根據當前的速度自動選擇合適的檔位:
無論何時設置 AutomaticCar
實例的 currentSpeed
屬性,其屬性中的 didSet
觀察器都會將實例中的 gear
屬性設置為適合速度的檔位:
# 防止覆寫
你可以透過將方法、屬性或下標標記為 final
來防止其被覆寫。在方法、屬性及下標的之前編寫 final
修飾符來達成此操作( 像是: final var
、 final func
、 final class func
和final subscript
)。
任何嘗試覆寫 subClass 中 final
方法、屬性或是下標都會表示編譯錯誤。你添加到擴展(extension)中的 class 的方法、屬性和下標也可以在擴展定義中標記為 final
。
你可以標記整個 class 為 final
(final class)。,透過編寫 final
修飾符在 class
關鍵字之前任何對 final class 做 subclass 的嘗試都會表示編輯錯誤。