繼承
蘋果官方文檔
原帖地址
一個類可以從另一個類繼承方法肿孵,屬性和其他的特性垒手。當(dāng)一個類從另一個類繼承的時候习柠,繼承類被稱為子類匀谣,這個類繼承的類被稱為父類。在Swift中津畸,繼承是基本的振定,從Swift中的其他類型來區(qū)分類的一種行為必怜。
在Swift中類可以調(diào)用和訪問方法肉拓,屬性和屬于它們父類的下標(biāo)腳本,并且提供它們自己重寫的方法梳庆,屬性和下標(biāo)腳本來定義或修改它們的行為暖途。Swift會確保你的重寫是正確的,通過檢查膏执,重寫定義都有一個與之匹配的父類定義驻售。
類也可以向繼承的屬性添加屬性監(jiān)聽者,當(dāng)屬性的值改變了以便通知欺栗〕偌福可以添加任何屬性到屬性監(jiān)聽者中消请,不管它是被定義為存儲或是計算屬性类腮。
定義基類
任何不從另一個類繼承的類都稱為基類。
注意:
Swift類不從一個通用基類繼承蚜枢。你定義的類不會自動地指定一個父類,你定義了沒有指定父類的類需频,在你創(chuàng)建時會自動成為基類筷凤。
下面的例子定義了一個叫做 Vehicle
的基類嵌施。這個基類定義了一個存儲屬性稱為 currentSpeed
吗伤,它有一個默認(rèn)值0足淆。0(推斷為一個Double
類型的屬性)巧号。currentSpeed
屬性的值被用在一個稱為description
的String
類型的只讀計算屬性來創(chuàng)建一個vehicle的描述丹鸿。
Vehicle
基類也定義了一個稱為makeNoise
的方法靠欢。這個方法實(shí)際上不會為這個基類的實(shí)例做任何事门怪,但是后面它可以被Vehicle
的子類自定義:
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
你使用初始化語法創(chuàng)建了一個新的Vehicle
實(shí)例掷空,寫為類型名后面跟著一個空括號:
let someVehicle = Vehicle()
創(chuàng)建了一個新的Vehicle
實(shí)例之后囤锉,你可以訪問它的description
屬性來輸出一個人類可讀的,vehicle
的當(dāng)前速度的描述:
print("Vehicle: \(someVehicle嚼锄。description)")
// Vehicle: traveling at 0.0 miles per hour
Vehicle
類為任意的車輛定義了共同的特點(diǎn)区丑,但是對它本身沒有太大用處沧侥。為了讓它更有用宴杀,你需要重定義它來描述更具體的車輛種類。
子類化
子類化是基于現(xiàn)有類的新類的行為扁达。子類從現(xiàn)有的類繼承了一些特性跪解,你可以重新定義它們叉讥。你也可以為子類添加新的特性图仓。
為了表示子類有父類,要把子類寫在父類的前面帚豪,用冒號隔開:
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
下面的例子定義了一個稱為Bicycle
的子類,繼承于Vehicle
:
class Bicycle: Vehicle {
var hasBasket = false
}
新的Bicycle
類獲得了Vehicle
的所有特性昌执,例如它的currentSpeed
和description
屬性和makeNoise()
方法煤禽。
除了繼承的特點(diǎn)檬果,Bicycle
類定義了一個新的存儲屬性hasBasket
选脊,并且默認(rèn)值為false
(推斷它的類型為Bool
)恳啥。
默認(rèn)情況钝的,任何你新建的Bicycle
實(shí)例都都不會有籃子。在一個特定的Bicycle
類實(shí)例創(chuàng)建之后亿柑,你可以為這個實(shí)例設(shè)置hasBasket
屬性為true
:
let bicycle = Bicycle()
bicycle望薄。hasBasket = true
你也可以在Bicycle
類實(shí)例中修改繼承的currentSpeed
屬性,或是查詢實(shí)例的繼承description
屬性:
bicycle卧须。currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
子類也可以被繼承花嘶。下個例子創(chuàng)建了一個Bicycle
類的子類,一個兩座的自行車稱為"tandem":
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem
從Bicycle
繼承了所有的屬性和方法隘击,也依次從Vehicle
繼承了所有屬性和方法埋同。Tandem
子類也添加了一個新的稱為currentNumberOfPassengers
的存儲屬性凶赁,并且有一個默認(rèn)值0:
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
重寫
一個子類可以提供它自己的實(shí)例方法楼熄,類型方法可岂,實(shí)例屬性,類型屬性或下標(biāo)腳本的自定義實(shí)現(xiàn)平斩,否則绘面,它將會從父類繼承揭璃。這就是重寫。
為了重寫一個被繼承的特性情组,你要在你的重寫定義前面加上override
關(guān)鍵詞院崇。這樣做說明你打算提供一個重寫,如果不這樣做就會匹配一個錯誤的定義濒持。意外的重寫可能導(dǎo)致意意想不到的行為柑营,并且當(dāng)你編譯的時候官套,對于任意沒有override
關(guān)鍵詞的重寫都會被視為錯誤。
override
關(guān)鍵詞也提供Swift編譯器檢查站刑,你的重寫類的父類(或者父類的父類)有一個聲明來匹配你提供的重寫绞旅。檢查確保你重寫的定義是是正確的。
訪問父類方法晃琳,屬性和下標(biāo)腳本
當(dāng)你為子類提供了一個方法卫旱,屬性或者下標(biāo)腳本,有時那是有用的暴构,來使用現(xiàn)有的父類實(shí)現(xiàn)作為你重寫的一部分取逾。例如,你可以重定義現(xiàn)有實(shí)現(xiàn)的行為晴埂,或者在現(xiàn)有的繼承變量中存儲一個修正值儒洛。
這是適當(dāng)?shù)呢酝#憧梢酝ㄟ^使用super
前綴惊完,訪問父類的方法,屬性或下標(biāo)腳本:
- 一個命名為
someMethod()
的重寫方法可以通過super凿跳。someMethod()
在重寫的方法實(shí)現(xiàn)中拄显,調(diào)用父類的someMethod()
方法。 - 一個命名為
someProperty
的重寫屬性當(dāng)通過super承边。someProperty
在重寫的getter或setter實(shí)現(xiàn)中,可以訪問父類的someProperty
屬性富岳。 - 一個命名為
someIndex
的重寫下標(biāo)腳本當(dāng)使用super[someIndex]
在重寫的下標(biāo)腳本實(shí)現(xiàn)中,可以訪問父類中相同的腳本萝喘。
重寫方法
你可以在你的子類中爬早,重寫一個繼承的實(shí)例或類型方法來提供定制的或是可選的方法實(shí)現(xiàn)筛严。
下面的例子定義了一個新的Vehicle
子類脑漫,稱為Train
吨拍,重寫了makeNoise()
方法:
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
如果你創(chuàng)建了一個新的Train實(shí)例伊滋,并且調(diào)用了makeNoise()
方法笑旺,你可以看到Train
子類的方法被調(diào)用了:
let train = Train()
train.makeNoise()
// prints "Choo Choo"
重寫屬性
你可以提供一個繼承實(shí)例或類型來為你自己的屬性提供你自己自定義的getter和setter,或者添加屬性監(jiān)聽者確保當(dāng)?shù)讓訉傩灾蹈淖儠r來監(jiān)聽重寫的屬性乌妙。
重寫屬性getter和setter
你可以提供一個自定義的getter(和setter,如果合適的話)來重寫任意的繼承屬性泽艘,不管在開始時繼承屬性是否實(shí)現(xiàn)了存儲屬性或計算屬性。一個繼承屬性的存儲或計算性質(zhì)不被看做一個子類--它僅被看做繼承屬性有某個名字或類型焕盟。你必須聲明你重寫的屬性名字和類型灼卢,來確保編譯器可以檢查你的重寫匹配了父類中有相同名字和類型的屬性鞋真。
通過在你的子類重寫屬性里提供getter和setter方法,你可以把一個繼承為只讀的屬性重寫為讀寫的屬性檩互。然而闸昨,你不能把繼承為讀寫的屬性重寫為只讀屬性。
注意:
如果你提供了一個setter作為一個屬性重寫的部分循诉,你也必須為重寫提供一個getter。如果你不想在重寫getter時修改繼承屬性的值募疮,那么你可以簡單通過從getter返回super。someProperty
繼承的值芭毙,someProperty
就是你重寫的屬性的名字。
下面的例子定義了一個新類Car
卸耘,繼承自Vehicle
類退敦。Car
類介紹了一個新的存儲屬性gear
,并且有一個默認(rèn)的整型值 1蚣抗。 Car
類也重寫了description
屬性侈百,它繼承自Vehicle
,提供了一個自定義的描述钝域,介紹了當(dāng)前的gear:
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)前的gear信息。
如果你創(chuàng)建一個Car
類的實(shí)例织咧,并且設(shè)置了它的setter
和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
重寫屬性監(jiān)聽者
你可以使用屬性重寫來添加屬性監(jiān)聽者為繼承的屬性。這就可以當(dāng)繼承的屬性值改變的時候通知你笙蒙,不管這個屬性最初是如何實(shí)現(xiàn)的抵屿。對于屬性監(jiān)聽者上更多的信息,可以看屬性監(jiān)聽者手趣。
注意:
你不能添加屬性監(jiān)聽者來繼承常量的存儲屬性或者繼承只讀的計算屬性晌该。這些屬性的值不能被set
(設(shè)置)肥荔,所以提供willSet
或didSet
實(shí)現(xiàn)作為重寫的一部分是不恰當(dāng)?shù)摹?/p>
也要注意绿渣,你不能為相同的屬性提供一個重寫的setter和一個重寫的屬性監(jiān)聽者。如果你想要監(jiān)聽屬性值的改變燕耿,并且你已經(jīng)為那個屬性提供了一個自定義的setter中符,那么你可以從自定義的setter里簡單地監(jiān)聽任意值的改變。
下面的例子定義了一個新類AutomaticCar
誉帅,繼承自Car
淀散。AutomaticCar
類代表一輛車有一個自動的變速箱(gearbox),可以根據(jù)當(dāng)前的速度自動地選擇一個合適的gear:
class AutomaticCar : Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
無論你在什么時候設(shè)置了AutomaticCar
實(shí)例的currentSpeed
屬性蚜锨,這個屬性的didSet
監(jiān)聽者都會設(shè)置實(shí)例的gear屬性給新的速度一個合適的值档插。具體地說,屬性監(jiān)聽者選擇了一個gear亚再,它是新的currentSpeed
值除以10郭膛,四舍五入到最近整數(shù),然后加1氛悬。例如则剃,速度是35,那么gear就是4:
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic如捅。description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
阻止重寫
你可以通過標(biāo)記為final
來阻止一個方法棍现,屬性或者下標(biāo)腳本被重寫。通過在方法镜遣,屬性或者下標(biāo)腳本的關(guān)鍵字前寫final
修飾符(例如己肮,final var
,final func
,final class func
谎僻,fianl subscript
)窖剑。
任何想要在子類里重寫final
方法,屬性或下標(biāo)腳本都會報一個編譯時錯誤戈稿。在擴(kuò)展中添加到一個類中的方法西土,屬性或下標(biāo)腳本也可以子擴(kuò)展的定義里被標(biāo)記為final
债鸡。
你可以標(biāo)記一個額外的類通過在class
關(guān)鍵字前面寫final
修飾符為final
(final class
)凄鼻。任何想要子類為一個final
類都會報一個編譯時錯誤。