繼承
一個類可以繼承另一個類的方法偿警,屬性和其它特性负间。當(dāng)一個類繼承其它類時,繼承類叫子類颖杏,被繼承類叫超類(或父類)纯陨。在 Swift 中,繼承是區(qū)分「類」與其它類型的一個基本特征留储。
在 Swift 中翼抠,類可以調(diào)用和訪問超類的方法、屬性和下標获讳,并且可以重寫這些方法阴颖,屬性和下標來優(yōu)化或修改它們的行為。Swift 會檢查你的重寫定義在超類中是否有匹配的定義丐膝,以此確保你的重寫行為是正確的量愧。
可以為類中繼承來的屬性添加屬性觀察器,這樣一來帅矗,當(dāng)屬性值改變時侠畔,類就會被通知到∷鹞睿可以為任何屬性添加屬性觀察器软棺,無論它原本被定義為存儲型屬性還是計算型屬性。
定義一個基類
不繼承于其它類的類尤勋,稱之為基類喘落。
注意
Swift 中的類并不是從一個通用的基類繼承而來的。如果你不為自己定義的類指定一個超類的話最冰,這個類就會自動成為基類瘦棋。
下面的例子定義了一個叫 Vehicle
的基類。這個基類聲明了一個名為 currentSpeed
暖哨,默認值是 0.0
的存儲型屬性(屬性類型推斷為 Double
)赌朋。currentSpeed
屬性的值被一個 String
類型的只讀計算型屬性 description
使用,用來創(chuàng)建對于車輛的描述篇裁。
Vehicle
基類還定義了一個名為 makeNoise
的方法沛慢。這個方法實際上不為 Vehicle
實例做任何事,但之后將會被 Vehicle
的子類定制:
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做——因為車輛不一定會有噪音
}
}
可以用初始化語法創(chuàng)建一個 Vehicle
的新實例达布,即類名后面跟一個空括號:
let someVehicle = Vehicle()
現(xiàn)在已經(jīng)創(chuàng)建了一個 Vehicle
的新實例团甲,你可以訪問它的 description
屬性來打印車輛的當(dāng)前速度:
print("Vehicle: \(someVehicle.description)")
// 打印“Vehicle: traveling at 0.0 miles per hour”
Vehicle
類定義了一個具有通用特性的車輛類,但實際上對于它本身來說沒什么用處黍聂。為了讓它變得更加有用躺苦,還需要進一步完善它身腻,從而能夠描述一個具體類型的車輛。
子類生成
子類生成指的是在一個已有類的基礎(chǔ)上創(chuàng)建一個新的類匹厘。子類繼承超類的特性嘀趟,并且可以進一步完善。你還可以為子類添加新的特性愈诚。
為了指明某個類的超類她按,將超類名寫在子類名的后面,用冒號分隔:
class SomeClass: SomeSuperclass {
// 這里是子類的定義
}
下一個例子扰路,定義了一個叫 Bicycle
的子類尤溜,繼承自超類 Vehicle
:
class Bicycle: Vehicle {
var hasBasket = false
}
新的 Bicycle
類自動繼承 Vehicle
類的所有特性倔叼,比如 currentSpeed
和 description
屬性汗唱,還有 makeNoise()
方法。
除了所繼承的特性丈攒,Bicycle
類還定義了一個默認值為 false
的存儲型屬性 hasBasket
(屬性推斷為 Bool
)哩罪。
默認情況下,你創(chuàng)建的所有新的 Bicycle
實例不會有一個籃子(即 hasBasket
屬性默認為 false
)巡验。創(chuàng)建該實例之后际插,你可以為 Bicycle
實例設(shè)置 hasBasket
屬性為 ture
:
let bicycle = Bicycle()
bicycle.hasBasket = true
你還可以修改 Bicycle
實例所繼承的 currentSpeed
屬性,和查詢實例所繼承的 description
屬性:
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// 打印“Bicycle: traveling at 15.0 miles per hour”
子類還可以繼續(xù)被其它類繼承显设,下面的示例為 Bicycle
創(chuàng)建了一個名為 Tandem
(雙人自行車)的子類:
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem
從 Bicycle
繼承了所有的屬性與方法框弛,這又使它同時繼承了 Vehicle
的所有屬性與方法。Tandem
也增加了一個新的叫做 currentNumberOfPassengers
的存儲型屬性捕捂,默認值為 0
瑟枫。
如果你創(chuàng)建了一個 Tandem
的實例,你可以使用它所有的新屬性和繼承的屬性指攒,還能查詢從 Vehicle
繼承來的只讀屬性 description
:
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// 打涌睹睢:“Tandem: traveling at 22.0 miles per hour”
重寫
子類可以為繼承來的實例方法,類方法允悦,實例屬性膝擂,類屬性,或下標提供自己定制的實現(xiàn)隙弛。我們把這種行為叫重寫架馋。
如果要重寫某個特性,你需要在重寫定義的前面加上 override
關(guān)鍵字全闷。這么做绩蜻,就表明了你是想提供一個重寫版本,而非錯誤地提供了一個相同的定義室埋。意外的重寫行為可能會導(dǎo)致不可預(yù)知的錯誤办绝,任何缺少 override
關(guān)鍵字的重寫都會在編譯時被認定為錯誤伊约。
override
關(guān)鍵字會提醒 Swift 編譯器去檢查該類的超類(或其中一個超類)是否有匹配重寫版本的聲明。這個檢查可以確保你的重寫定義是正確的孕蝉。
訪問超類的方法屡律,屬性及下標
當(dāng)你在子類中重寫超類的方法,屬性或下標時降淮,有時在你的重寫版本中使用已經(jīng)存在的超類實現(xiàn)會大有裨益超埋。比如,你可以完善已有實現(xiàn)的行為佳鳖,或在一個繼承來的變量中存儲一個修改過的值霍殴。
在合適的地方,你可以通過使用 super
前綴來訪問超類版本的方法系吩,屬性或下標:
在方法
someMethod()
的重寫實現(xiàn)中来庭,可以通過super.someMethod()
來調(diào)用超類版本的someMethod()
方法。在屬性
someProperty
的 getter 或 setter 的重寫實現(xiàn)中穿挨,可以通過super.someProperty
來訪問超類版本的someProperty
屬性月弛。在下標的重寫實現(xiàn)中,可以通過
super[someIndex]
來訪問超類版本中的相同下標科盛。
重寫方法
在子類中帽衙,你可以重寫繼承來的實例方法或類方法,提供一個定制或替代的方法實現(xiàn)贞绵。
下面的例子定義了 Vehicle
的一個新的子類厉萝,叫 Train
,它重寫了從 Vehicle
類繼承來的 makeNoise()
方法:
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
如果你創(chuàng)建一個 Train
的新實例榨崩,并調(diào)用了它的 makeNoise()
方法谴垫,你就會發(fā)現(xiàn) Train
版本的方法被調(diào)用:
let train = Train()
train.makeNoise()
// 打印“Choo Choo”
重寫屬性
你可以重寫繼承來的實例屬性或類型屬性,提供自己定制的 getter 和 setter蜡饵,或添加屬性觀察器弹渔,使重寫的屬性可以觀察到底層的屬性值什么時候發(fā)生改變。
重寫屬性的 Getters 和 Setters
你可以提供定制的 getter(或 setter)來重寫任何一個繼承來的屬性溯祸,無論這個屬性是存儲型還是計算型屬性肢专。子類并不知道繼承來的屬性是存儲型的還是計算型的,它只知道繼承來的屬性會有一個名字和類型焦辅。你在重寫一個屬性時博杖,必須將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的筷登。
你可以將一個繼承來的只讀屬性重寫為一個讀寫屬性剃根,只需要在重寫版本的屬性里提供 getter 和 setter 即可。但是前方,你不可以將一個繼承來的讀寫屬性重寫為一個只讀屬性狈醉。
注意
如果你在重寫屬性中提供了 setter廉油,那么你也一定要提供 getter。如果你不想在重寫版本中的 getter 里修改繼承來的屬性值苗傅,你可以直接通過
super.someProperty
來返回繼承來的值抒线,其中someProperty
是你要重寫的屬性的名字。
以下的例子定義了一個新類渣慕,叫 Car
嘶炭,它是 Vehicle
的子類。這個類引入了一個新的存儲型屬性叫做 gear
逊桦,默認值為整數(shù) 1
眨猎。Car
類重寫了繼承自 Vehicle
的 description
屬性,提供包含當(dāng)前檔位的自定義描述:
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
重寫的 description
屬性首先要調(diào)用 super.description
返回 Vehicle
類的 description
屬性强经。之后睡陪,Car
類版本的 description
在末尾增加了一些額外的文本來提供關(guān)于當(dāng)前檔位的信息。
如果你創(chuàng)建了 Car
的實例并且設(shè)置了它的 gear
和 currentSpeed
屬性夕凝,你可以看到它的 description
返回了 Car
中的自定義描述:
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// 打印“Car: traveling at 25.0 miles per hour in gear 3”
重寫屬性觀察器
你可以通過重寫屬性為一個繼承來的屬性添加屬性觀察器宝穗。這樣一來户秤,無論被繼承屬性原本是如何實現(xiàn)的码秉,當(dāng)其屬性值發(fā)生改變時,你就會被通知到鸡号。關(guān)于屬性觀察器的更多內(nèi)容转砖,請看 屬性觀察器。
注意
你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器鲸伴。這些屬性的值是不可以被設(shè)置的府蔗,所以,為它們提供
willSet
或didSet
實現(xiàn)也是不恰當(dāng)汞窗。 此外還要注意姓赤,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化仲吏,并且你已經(jīng)為那個屬性提供了定制的 setter不铆,那么你在 setter 中就可以觀察到任何值變化了。
下面的例子定義了一個新類叫 AutomaticCar
裹唆,它是 Car
的子類誓斥。AutomaticCar
表示自動檔汽車,它可以根據(jù)當(dāng)前的速度自動選擇合適的檔位:
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
當(dāng)你設(shè)置 AutomaticCar
的 currentSpeed
屬性许帐,屬性的 didSet
觀察器就會自動地設(shè)置 gear
屬性劳坑,為新的速度選擇一個合適的檔位。具體來說就是成畦,屬性觀察器將新的速度值除以 10
距芬,然后向下取得最接近的整數(shù)值涝开,最后加 1
來得到檔位 gear
的值。例如框仔,速度為 35.0
時忠寻,檔位為 4
:
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// 打印“AutomaticCar: traveling at 35.0 miles per hour in gear 4”
防止重寫
你可以通過把方法,屬性或下標標記為 final 來防止它們被重寫存和,只需要在聲明關(guān)鍵字前加上 final
修飾符即可(例如:final var
奕剃、final func
、final class func
以及 final subscript
)捐腿。
任何試圖對帶有 final
標記的方法纵朋、屬性或下標進行重寫的代碼,都會在編譯時會報錯茄袖。在類擴展中的方法操软,屬性或下標也可以在擴展的定義里標記為 final
。
可以通過在關(guān)鍵字 class
前添加 final
修飾符(final class
)來將整個類標記為 final 宪祥。這樣的類是不可被繼承的聂薪,試圖繼承這樣的類會導(dǎo)致編譯報錯。
繼續(xù)閱讀 Swift - 構(gòu)造過程