Swift4.2語言指南(十五) 繼承
類可以從另一個類繼承方法,屬性和其他特性。當一個類繼承自另一個類時,繼承類稱為子類,它繼承的類稱為其超類。繼承是一種基本行為,它將類與Swift中的其他類型區分開來。
Swift中的類可以調用和訪問屬於其超類的方法,屬性和下標,並可以提供這些方法,屬性和下標的自己的重寫版本,以優化或修改它們的行為。Swift通過檢查覆蓋定義是否具有匹配的超類定義來幫助確保覆蓋是正確的。
類還可以將屬性觀察器添加到繼承的屬性,以便在屬性值更改時得到通知。屬性觀察器可以添加到任何屬性,無論它最初是被定義為存儲屬性還是計算屬性。
定義基類
任何不從其他類繼承的類都稱為基類。
註意
Swift類不從通用基類繼承。
下面的示例定義了一個名為的基類Vehicle
。此基類定義一個名為的存儲屬性currentSpeed
,其默認值為0.0
(推斷屬性類型Double
)。該currentSpeed
屬性的值由一個只讀的計算String
屬性使用,該屬性被調用description
以創建車輛的描述。
所述Vehicle
基類還定義了一個稱為方法makeNoise
。此方法實際上並不對基本Vehicle
實例執行任何操作,但將由Vehicle
以後的子類自定義:
1 class Vehicle { 2 var currentSpeed = 0.0 3 var description: String {4 return "traveling at \(currentSpeed) miles per hour" 5 } 6 func makeNoise() { 7 // do nothing - an arbitrary vehicle doesn‘t necessarily make a noise 8 } 9 }
您Vehicle
使用初始化程序語法創建一個新實例,該語法寫為類型名稱,後跟空括號:
let someVehicle = Vehicle()
創建新Vehicle
實例後,您可以訪問其description
1 print("Vehicle: \(someVehicle.description)") 2 // Vehicle: traveling at 0.0 miles per hour
在Vehicle
類定義的共同特點為任意車輛,但沒有太大用處本身。為了使其更有用,您需要對其進行優化以描述更具體的車輛類型。
子類
子類化是在現有類上創建新類的行為。子類繼承現有類的特征,然後可以對其進行細化。您還可以向子類添加新特征。
要指示子類具有超類,請在超類名稱之前寫入子類名稱,用冒號分隔:
1 class SomeSubclass: SomeSuperclass { 2 // subclass definition goes here 3 }
以下示例定義了一個名為的子類Bicycle
,其超類為Vehicle
:
1 class Bicycle: Vehicle { 2 var hasBasket = false 3 }
新的Bicycle
類自動獲得所有的特性Vehicle
,例如它的currentSpeed
和description
特性及其makeNoise()
方法。
除了它繼承的特性之外,Bicycle
該類還定義了一個新的存儲屬性,hasBasket
其默認值為false
(推斷Bool
屬性的類型)。
默認情況下,Bicycle
您創建的任何新實例都不會有籃子。您可以在創建該實例後為特定實例設置hasBasket
屬性:true
Bicycle
1 let bicycle = Bicycle() 2 bicycle.hasBasket = true
您還可以修改繼承的currentSpeed
一個財產Bicycle
實例,查詢實例的繼承description
財產:
1 bicycle.currentSpeed = 15.0 2 print("Bicycle: \(bicycle.description)") 3 // Bicycle: traveling at 15.0 miles per hour
子類本身可以是子類。下一個例子Bicycle
為雙座自行車創建一個子類,稱為“串聯”:
1 class Tandem: Bicycle { 2 var currentNumberOfPassengers = 0 3 }
Tandem
繼承了所有屬性和方法Bicycle
,從而繼承了所有的屬性和方法Vehicle
。該Tandem
子類還增加了一個新的存儲屬性調用currentNumberOfPassengers
,用的默認值0
。
如果您創建了一個實例Tandem
,則可以使用其任何新的和繼承的屬性,並查詢description
它繼承的只讀屬性Vehicle
:
1 let tandem = Tandem() 2 tandem.hasBasket = true 3 tandem.currentNumberOfPassengers = 2 4 tandem.currentSpeed = 22.0 5 print("Tandem: \(tandem.description)") 6 // Tandem: traveling at 22.0 miles per hour
重寫
子類可以提供自己的實例方法,類型方法,實例屬性,類型屬性或下標的自定義實現,否則它將從超類繼承。這被稱為重寫。
要覆蓋否則將繼承的特性,請使用override
關鍵字為覆蓋定義添加前綴。這樣做可以澄清您打算提供覆蓋並且沒有錯誤地提供匹配的定義。意外覆蓋可能會導致意外行為,並且override
在編譯代碼時,沒有關鍵字的任何覆蓋都會被診斷為錯誤。
該override
關鍵字還會提示Swift編譯器檢查您的重寫類的超類(或其父類之一)是否具有與您為重寫提供的聲明匹配的聲明。此檢查可確保您的重寫定義正確無誤。
訪問超類方法,屬性和下標
當您為子類提供方法,屬性或下標覆蓋時,將現有超類實現用作覆蓋的一部分有時很有用。例如,您可以優化現有實現的行為,或將修改後的值存儲在現有的繼承變量中。
如果這是合適的,您可以使用super
前綴訪問方法,屬性或下標的超類版本:
- 名為overridden的方法
someMethod()
可以someMethod()
通過super.someMethod()
在重寫方法實現中調用來調用超類版本。 - 被稱為覆蓋的屬性
someProperty
可以訪問的超類版本someProperty
作為super.someProperty
壓倒一切的getter或setter實現中。 - 重寫的下標
someIndex
可以super[someIndex]
從重寫的下標實現中訪問相同下標的超類版本。
重寫方法
您可以覆蓋繼承的實例或類型方法,以在子類中提供方法的定制或替代實現。
以下示例定義了一個新的Vehicle
被調用子類Train
,它覆蓋了從以下makeNoise()
方法Train
繼承的方法Vehicle
:
1 class Train: Vehicle { 2 override func makeNoise() { 3 print("Choo Choo") 4 } 5 }
如果您創建一個新實例Train
並調用其makeNoise()
方法,則可以看到該方法的Train
子類版本被調用:
1 let train = Train() 2 train.makeNoise() 3 // Prints "Choo Choo"
覆蓋屬性
您可以覆蓋繼承的實例或類型屬性,以便為該屬性提供自己的自定義getter和setter,或添加屬性觀察器以使覆蓋屬性能夠在基礎屬性值更改時進行觀察。
覆蓋屬性getter和setter
您可以提供自定義getter(和setter,如果適用)以覆蓋任何繼承的屬性,無論繼承的屬性是在源上實現為存儲屬性還是計算屬性。子類不知道繼承屬性的存儲或計算性質 - 它只知道繼承的屬性具有特定的名稱和類型。您必須始終聲明要覆蓋的屬性的名稱和類型,以使編譯器能夠檢查您的覆蓋是否與具有相同名稱和類型的超類屬性匹配。
通過在子類屬性覆蓋中提供getter和setter,可以將繼承的只讀屬性作為讀寫屬性提供。但是,您不能將繼承的讀寫屬性顯示為只讀屬性。
註意
如果您將setter作為屬性覆蓋的一部分提供,則還必須為該覆蓋提供getter。如果您不想在重寫getter中修改繼承屬性的值,則可以通過super.someProperty
從getter 返回來簡單地傳遞繼承的值,其中someProperty
是您要覆蓋的屬性的名稱。
以下示例定義了一個名為的新類Car
,它是一個子類Vehicle
。該Car
課程介紹了新的存儲屬性調用gear
,用默認整數值1
。的Car
類也覆蓋了description
它從繼承屬性Vehicle
,以提供包括當前齒輪定制描述:
1 class Car: Vehicle { 2 var gear = 1 3 override var description: String { 4 return super.description + " in gear \(gear)" 5 } 6 }
description
屬性的覆蓋從調用開始super.description
,它返回Vehicle
類的description
屬性。然後,Car
該類的版本description
在本說明書的末尾添加了一些額外的文本,以提供有關當前檔位的信息。
如果你創建的實例Car
類,並設置它gear
和currentSpeed
屬性,你可以看到它的description
屬性返回中定義的定制描述Car
類:
1 let car = Car() 2 car.currentSpeed = 25.0 3 car.gear = 3 4 print("Car: \(car.description)") 5 // Car: traveling at 25.0 miles per hour in gear 3
覆蓋屬性觀察器
您可以使用屬性覆蓋將屬性觀察器添加到繼承的屬性。這使您可以在繼承屬性的值更改時收到通知,無論該屬性最初如何實現。有關屬性觀察器的更多信息,請參閱Property Observers。
註意
您不能將屬性觀察器添加到繼承的常量存儲屬性或繼承的只讀計算屬性。無法設置這些屬性的值,因此不適合將提供willSet
或didSet
實現作為覆蓋的一部分。
另請註意,您不能同時為同一屬性提供重寫setter和覆蓋屬性觀察器。如果要觀察屬性值的更改,並且您已經為該屬性提供了自定義setter,則只需觀察自定義setter中的任何值更改即可。
以下示例定義了一個名為的新類AutomaticCar
,它是一個子類Car
。所述AutomaticCar
類表示用自動變速器,自動選擇適當的齒輪使用基於當前速度一輛車:
1 class AutomaticCar: Car { 2 override var currentSpeed: Double { 3 didSet { 4 gear = Int(currentSpeed / 10.0) + 1 5 } 6 } 7 }
當你設置currentSpeed
一個的屬性AutomaticCar
實例屬性的didSet
觀察器設置實例的gear
屬性齒輪的新速度一個合適的選擇。具體來說,屬性觀察器選擇一個新currentSpeed
值除以的齒輪,10
向下舍入到最接近的整數,加號1
。35.0
生產齒輪的速度4
:
1 let automatic = AutomaticCar() 2 automatic.currentSpeed = 35.0 3 print("AutomaticCar: \(automatic.description)") 4 // AutomaticCar: traveling at 35.0 miles per hour in gear 4
防止覆蓋
您可以通過將方法,屬性或下標標記為final來阻止它被覆蓋。通過寫做到這一點final
的方法,屬性,或標的介紹人關鍵字之前修飾符(如,,,和)。final var
final func
final class func
final subscript
任何覆蓋子類中的最終方法,屬性或下標的嘗試都會報告為編譯時錯誤。您添加到擴展中的類的方法,屬性或下標也可以在擴展的定義中標記為final。
您可以通過在類定義(final class)中的final
class
關鍵字之前編寫修飾符來將整個類標記為final 。任何將最終類子類化的嘗試都會報告為編譯時錯誤。final class
Swift4.2語言指南(十五) 繼承