Swift教程之繼承詳解
一個(gè)類可以從另外一個(gè)類中繼承方法,屬性或者其它的一些特性。當(dāng)一個(gè)類繼承于另外一個(gè)類時(shí),這個(gè)繼承的類叫子類,被繼承的類叫父類。繼承是Swift中類區(qū)別于其它類型的一個(gè)基本特征。
Swift中的類可以調(diào)用父類的方法,使用父類的屬性和下標(biāo),還可以根據(jù)需要使用重寫方法或者屬性來重新定義和修改他們的一些特性。Swift可以幫助你檢查重寫的方法和父類的方法定義是相符的。
類還可以為它繼承的屬性添加觀察者,這樣可以能夠讓它在一個(gè)屬性變化的時(shí)候得到通知。屬性觀察者可以被添加給任何屬性,不管它之前是存儲屬性還是計(jì)算屬性。
1、定義一個(gè)基類
任何一個(gè)不繼承于其它類的類被稱作基類
注意:Swift的類不是從一個(gè)全局基類繼承而來。在你編寫代碼的時(shí),只要是在類的定義中沒有繼承自父類的類都是基類。
下面的例子定義了一個(gè)叫Vehicle的基類?;惏瑑蓚€(gè)所有交通工具通用的屬性numberOfWheels和maxPassengers。這兩個(gè)屬性被一個(gè)叫description的方法使用,通過返回一個(gè)String描述來作為這個(gè)交通工具的特征:
class Vehicle {
var numberOfWheels: Int
var maxPassengers: Int
func description() -> String {
return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
}
init() {
numberOfWheels = 0
maxPassengers = 1
}
}
這個(gè)交通工具類Vehicle還定義了一個(gè)構(gòu)造函數(shù)來設(shè)置它的屬性。構(gòu)造函數(shù)更多的解釋在Initialization一章,但是為了說明子類如何修改繼承的屬性,這里需要簡要解釋一下什么叫構(gòu)造函數(shù)。
通過構(gòu)造函數(shù)可以創(chuàng)建一個(gè)類型的實(shí)例。盡管構(gòu)造函數(shù)不是方法,但是它們在編碼的時(shí)候使用了非常相似的語法。構(gòu)造函數(shù)通過確保所有實(shí)例的屬性都是有效的來創(chuàng)建一個(gè)新的實(shí)例。
構(gòu)造函數(shù)最簡單的形式是使用init關(guān)鍵詞的一個(gè)類似方法的函數(shù),并且沒有任何參數(shù):
init() {
// perform some initialization here
}
使用構(gòu)造函數(shù)語法TypeName和空的兩個(gè)小括號來完成一個(gè)Vehicle實(shí)例的創(chuàng)建:
let someVehicle = Vehicle()
Vehicle的構(gòu)造函數(shù)為屬性設(shè)置了一些初始值(numberOfWheels = 0 然后 maxPassengers = 1)。
Vehicle類定義的是一個(gè)通用的交通工具特性,它本身沒有太多意義,所以就需要沖定義它的一些屬性或者方法來讓它具有實(shí)際的意義。
2、產(chǎn)生子類
產(chǎn)生子類就是根據(jù)一個(gè)已有的類產(chǎn)生新類的過程。子類繼承了父類的一些可以修改的特性。還可以為子類添加一些新的特性。
為了表明一個(gè)類是繼承自一個(gè)父類,需要將父類的名稱寫在子類的后面,并且用冒號分隔:
class SomeClass: SomeSuperclass {
// class definition goes here
}
下面的例子定義了一種特定叫Bicycle的交通工具。這個(gè)新類是基于已有的類Vehicle產(chǎn)生的。書寫方式是在類名Bicycle后加冒號加父類Vehicle名。
可以理解為:
定義一個(gè)新的類叫Bicycle,它繼承了Vehicle的特性:
class Bicycle: Vehicle {
init() {
super.init()
numberOfWheels = 2
}
}
Bicycle是Vehicle的子類,Vehicle是Bicycle的父類。Bicycle類繼承了Vehicle所有的特征,比如maxPassengers和numberOfWheels屬性。你還可以為Bicycle類添加心的屬性。
Bicycle類也定義了構(gòu)造函數(shù),在這個(gè)構(gòu)造函數(shù)中調(diào)用了父類的構(gòu)造函數(shù)super.init(),這樣可以確保在Bicycle修改他們之前,父類已經(jīng)初始化了。
注意:跟Objective-C不同的是,Swift中的構(gòu)造函數(shù)沒有默認(rèn)繼承。更多信息可以參考Initializer Inheritance and Overriding這一章節(jié)。
maxPassengers屬性在繼承自父類的時(shí)候已經(jīng)被初始化了,對于Bicycle來說是正確的,因此不需要再做更改。然后numberOfWheels是不對的,所以被替換成了2.
不僅屬性是繼承于Vehicle的,Bicycle還繼承了父類的方法。如果你創(chuàng)建一個(gè)實(shí)例,然后調(diào)用了已經(jīng)繼承的description方法,可以得到該交通工具的描述并且看到它的屬性已經(jīng)被修改:
let bicycle = Bicycle()
println("Bicycle: \(bicycle.description())")
// Bicycle: 2 wheels; up to 1 passengers
子類本身也可以作為父類被再次繼承:
class Tandem: Bicycle {
init() {
super.init()
maxPassengers = 2
}
}
上面的例子創(chuàng)建了Bicycle的子類,叫做tandem,也就可以兩個(gè)人一起騎的自行車。所以Tandem沒有修改numberOfWheels屬性,只是更新了maxPassengers屬性。
注意:子類只能夠在構(gòu)造的時(shí)候修改變量的屬性,不能修改常量的屬性。
創(chuàng)建一個(gè)Tandem的實(shí)例,然后調(diào)用description方法檢查屬性是否被正確修改:
let tandem = Tandem()
println("Tandem: \(tandem.description())")
// Tandem: 2 wheels; up to 2 passengers
注意到description方法也被Tandem繼承了。
3、重寫方法
子類可以提供由父類繼承來的實(shí)例方法,類方法,實(shí)例屬性或者下標(biāo)的個(gè)性化實(shí)現(xiàn)。這個(gè)特性被稱為重寫。
重寫一個(gè)由繼承而來的方法需要在方法定義前標(biāo)注override關(guān)鍵詞。通過這樣的操作可以確保你所要修改的這個(gè)方法確實(shí)是繼承而來的,而不會出現(xiàn)重寫錯(cuò)誤。錯(cuò)誤的重寫會造成一些不可預(yù)知的錯(cuò)誤,所以如果如果不標(biāo)記override關(guān)鍵詞的話,就會被在代碼編譯時(shí)報(bào)錯(cuò)。
override關(guān)鍵詞還能夠讓Swift編譯器檢查該類的父類是否有相符的方法,以確保你的重寫是可用的,正確的。
訪問父類方法,屬性和下標(biāo)
當(dāng)在重寫子類繼承自父類的方法,屬性或者下標(biāo)的時(shí)候,需要用到一部分父類已有的實(shí)現(xiàn)。比如你可以重定義已知的一個(gè)實(shí)現(xiàn)或者在繼承的變量中存儲一個(gè)修改的值。
適當(dāng)?shù)臅r(shí)候,可以通過使用super前綴來訪問父類的方法,屬性或者下標(biāo):
叫someMethod的重寫方法可以在實(shí)現(xiàn)的時(shí)候通過super.someMethod()調(diào)用父類的someMethod方法。
叫someProperty的重寫屬性可以在重寫實(shí)現(xiàn)getter或者setter的時(shí)候通過super.someProperty調(diào)用父類的someProperty。
叫someIndex的重寫下標(biāo)可以在實(shí)現(xiàn)下標(biāo)的時(shí)候通過super[someIndex]來訪問父類的下標(biāo)。
復(fù)寫方法
你可以在你的子類中實(shí)現(xiàn)定制的繼承于父類的實(shí)例方法或者類方法。
下面的例子演示的就是一個(gè)叫Car的Vehicle子類,重寫了繼承自Vehicle的description方法。
class Car: Vehicle {
var speed: Double = 0.0
init() {
super.init()
maxPassengers = 5
numberOfWheels = 4
}
override func description() -> String {
return super.description() + "; "
+ "traveling at \(speed) mph"
}
}
Car中定義了一個(gè)新的Double類型的存儲屬性speed。這個(gè)屬性默認(rèn)值是0.0,意思是每小時(shí)0英里。Car還有一個(gè)自定義的構(gòu)造函數(shù),設(shè)置了最大乘客數(shù)為5,輪子數(shù)量是4.
Car重寫了繼承的description方法,并在方法名description前標(biāo)注了override關(guān)鍵詞。
在description中并沒有給出了一個(gè)全新的描述實(shí)現(xiàn),還是通過super.description使用了Vehicle提供的部分描述語句,然后加上了自己定義的一些屬性,如當(dāng)前速度。
如果你創(chuàng)建一個(gè)Car的實(shí)例,然后調(diào)用description方法,會發(fā)現(xiàn)描述語句變成了這樣:
let car = Car()
println("Car: \(car.description())")
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph
復(fù)寫屬性
你還可以提供繼承自父類的實(shí)例屬性或者類屬性的個(gè)性化getter和setter方法,或者是添加屬性觀察者來實(shí)現(xiàn)重寫的屬性可以觀察到繼承屬性的變動。
重寫屬性的Getters和Setters
不管在源類中繼承的這個(gè)屬性是存儲屬性還是計(jì)算屬性,你都可以提供一個(gè)定制的getter或者setter方法來重寫這個(gè)繼承屬性。子類一般不會知道這個(gè)繼承的屬性本來是存儲屬性還是計(jì)算屬性,但是它知道這個(gè)屬性有特定的名字和類型。在重寫的時(shí)候需要指明屬性的類型和名字,好讓編譯器可以檢查你的重寫是否與父類的屬性相符。
你可以將一個(gè)只讀的屬性通過提那家getter和setter繼承為可讀寫的,但是反之不可。
注意:如果你為一個(gè)重寫屬性提供了setter方法,那么也需要提供getter方法。如果你不想在getter中修改繼承的屬性的值,可以在getter中使用super.someProperty即可,在下面SpeedLimitedCar例子中也是這樣。
下面的例子定義了一個(gè)新類SpeedLimitedCar,是Car的一個(gè)子類。這個(gè)類表示一個(gè)顯示在40碼一下的車輛。通過重寫繼承的speed屬性來實(shí)現(xiàn):
class SpeedLimitedCar: Car {
override var speed: Double {
get {
return super.speed
}
set {
super.speed = min(newValue, 40.0)
}
}
}
每當(dāng)你要設(shè)置speed屬性的時(shí)候,setter都會檢查新值是否比40大,二者中較小的值會被設(shè)置給SpeedLimitedCar。
如果你嘗試為speed設(shè)置超過40的值,description的輸出依然還是40:
let limitedCar = SpeedLimitedCar()
limitedCar.speed = 60.0
println("SpeedLimitedCar: \(limitedCar.description())")
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph
重寫屬性觀察者
你可以使用屬性重寫為繼承的屬性添加觀察者。這種做法可以讓你無論這個(gè)屬性之前是如何實(shí)現(xiàn)的,在繼承的這個(gè)屬性變化的時(shí)候都能得到提醒。更多相關(guān)的信息可以參考Property Observers這章。
注意:不能為繼承的常量存儲屬性或者是只讀計(jì)算屬性添加觀察者。這些屬性值是不能被修改的,因此不適合在重寫實(shí)現(xiàn)時(shí)添加willSet或者didSet方法。
注意:不能同時(shí)定義重寫setter和重寫屬性觀察者,如果想要觀察屬性值的變化,并且又為該屬性給出了定制的setter,那只需要在setter中直接獲得屬性值的變化就行了。
下面的代碼演示的是一個(gè)新類AutomaticCar,也是Car的一個(gè)子類。這個(gè)類表明一個(gè)擁有自動變速箱的汽車,可以根據(jù)現(xiàn)在的速度自動選擇檔位,并在description中輸出當(dāng)前檔位:
class AutomaticCar: Car {
var gear = 1
override var speed: Double {
didSet {
gear = Int(speed / 10.0) + 1
}
}
override func description() -> String {
return super.description() + " in gear \(gear)"
}
}
這樣就可以實(shí)現(xiàn),每次你設(shè)置speed的值的時(shí)候,didSet方法都會被調(diào)用,來看檔位是否需要變化。gear是由speed除以10加1計(jì)算得來,所以當(dāng)速度為35的時(shí)候,gear檔位為4:
let automatic = AutomaticCar()
automatic.speed = 35.0
println("AutomaticCar: \(automatic.description())")
// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4
4、禁止重寫
你可以通過標(biāo)記final關(guān)鍵詞來禁止重寫一個(gè)類的方法,屬性或者下標(biāo)。在定義的關(guān)鍵詞前面標(biāo)注@final屬性即可。
在子類中任何嘗試重寫父類的final方法,屬性或者下標(biāo)的行為都會在編譯時(shí)報(bào)錯(cuò)。同樣在擴(kuò)展中為類添加的方法,屬性或者下標(biāo)也可以被標(biāo)記為final。
還可以在類關(guān)鍵詞class前使用@final標(biāo)記一整個(gè)類為final(@final class)。任何子類嘗試?yán)^承這個(gè)父類時(shí)都會在編譯時(shí)報(bào)錯(cuò)。
相關(guān)文章
RxSwift發(fā)送及訂閱 Subjects、Variables代碼示例
這篇文章主要介紹了RxSwift發(fā)送及訂閱 Subjects、Variables代碼示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12iOS中Swift指觸即開集成Touch ID指紋識別功能的方法
隨著移動支付時(shí)代的到來,Touch ID 指紋驗(yàn)證迅速被支付寶,微信錢包普及,相信各位朋友使用后也大呼方便。下面給大家分享iOS中Swift指觸即開集成Touch ID指紋識別功能的方法,一起看看吧2017-03-03Swift中static和class關(guān)鍵字的深入講解
這篇文章主要給大家介紹了關(guān)于Swift中static和class關(guān)鍵字的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Swift中風(fēng)味各異的類型擦除實(shí)例詳解
你也許曾聽過類型擦除,甚至也使用過標(biāo)準(zhǔn)庫提供的類型擦除類型如 AnySequence,下面這篇文章主要給大家介紹了關(guān)于Swift中風(fēng)味各異的類型擦除的相關(guān)資料,需要的朋友可以參考下2022-04-04SwiftUI 登錄界面布局實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了SwiftUI 登錄界面布局實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09