Swift重寫
重寫(Overriding)
子類可以爲繼承來的實例方法(instance method),類方法(class method),實例屬性(instance property),或下標腳本(subscript)提供自己定製的實現(implementation)。我們把這種行爲叫*重寫(overriding)*。
如果要重寫某個特性,你需要在重寫定義的前面加上override
關鍵字。這麼做,你就表明了你是想提供一個重寫版本,而非錯誤地提供了一個相同的定義。意外的重寫行爲可能會導致不可預知的錯誤,任何缺少override
關鍵字的重寫都會在編譯時被診斷爲錯誤。
override
關鍵字會提醒 Swift 編譯器去檢查該類的超類(或其中一個父類)是否有匹配重寫版本的聲明。這個檢查可以確保你的重寫定義是正確的。
訪問超類的方法,屬性及下標腳本
當你在子類中重寫超類的方法,屬性或下標腳本時,有時在你的重寫版本中使用已經存在的超類實現會大有裨益。比如,你可以優化已有實現的行爲,或在一個繼承來的變量中存儲一個修改過的值。
在合適的地方,你可以通過使用super
前綴來訪問超類版本的方法,屬性或下標腳本:
- 在方法
someMethod
的重寫實現中,可以通過super.someMethod()
來調用超類版本的someMethod
方法。 - 在屬性
someProperty
的 getter 或 setter 的重寫實現中,可以通過super.someProperty
來訪問超類版本的someProperty
屬性。 - 在下標腳本的重寫實現中,可以通過
super[someIndex]
來訪問超類版本中的相同下標腳本。
重寫方法
在子類中,你可以重寫繼承來的實例方法或類方法,提供一個定製或替代的方法實現。
下面的例子定義了Vehicle
的一個新的子類,叫Car
,它重寫了從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
聲明瞭一個新的存儲型屬性speed
,它是Double
類型的,默認值是0.0
,表示「時速是0英里」。Car
有自己的初始化器,它將乘客的最大數量設爲5,輪子數量設爲4。
Car
重寫了繼承來的description
方法,它的聲明與Vehicle
中的description
方法一致,聲明前面加上了override
關鍵字。
Car
中的description
方法並非完全自定義,而是通過super.description
使用了超類Vehicle
中的description
方法,然後再追加一些額外的信息,比如汽車的當前速度。
如果你創建一個Car
的新實例,並打印description
方法的輸出,你就會發現描述信息已經發生了改變:
let car = Car()
println("Car: \(car.description())")
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph
重寫屬性
你可以重寫繼承來的實例屬性或類屬性,提供自己定製的getter和setter,或添加屬性觀察器使重寫的屬性觀察屬性值什麼時候發生改變。
重寫屬性的Getters和Setters
你可以提供定製的 getter(或 setter)來重寫任意繼承來的屬性,無論繼承來的屬性是存儲型的還是計算型的屬性。子類並不知道繼承來的屬性是存儲型的還是計算型的,它只知道繼承來的屬性會有一個名字和類型。你在重寫一個屬性時,必需將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的。
你可以將一個繼承來的只讀屬性重寫爲一個讀寫屬性,只需要你在重寫版本的屬性裏提供 getter 和 setter 即可。但是,你不可以將一個繼承來的讀寫屬性重寫爲一個只讀屬性。
注意:
如果你在重寫屬性中提供了 setter,那麼你也一定要提供 getter。如果你不想在重寫版本中的 getter 裏修改繼承來的屬性值,你可以直接返回super.someProperty
來返回繼承來的值。正如下面的SpeedLimitedCar
的例子所示。
以下的例子定義了一個新類,叫SpeedLimitedCar
,它是Car
的子類。類SpeedLimitedCar
表示安裝了限速裝置的車,它的最高速度只能達到40mph。你可以通過重寫繼承來的speed
屬性來實現這個速度限制:
class SpeedLimitedCar: Car {
override var speed: Double {
get {
return super.speed
}
set {
super.speed = min(newValue, 40.0)
}
}
}
當你設置一個SpeedLimitedCar
實例的speed
屬性時,屬性setter的實現會去檢查新值與限制值40mph的大小,它會將超類的speed
設置爲newValue
和40.0
中較小的那個。這兩個值哪個較小由min
函數決定,它是Swift標準庫中的一個全局函數。min
函數接收兩個或更多的數,返回其中最小的那個。
如果你嘗試將SpeedLimitedCar
實例的speed
屬性設置爲一個大於40mph的數,然後打印description
函數的輸出,你會發現速度被限制在40mph:
let limitedCar = SpeedLimitedCar()
limitedCar.speed = 60.0
println("SpeedLimitedCar: \(limitedCar.description())")
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph
重寫屬性觀察器(Property Observer)
你可以在屬性重寫中爲一個繼承來的屬性添加屬性觀察器。這樣一來,當繼承來的屬性值發生改變時,你就會被通知到,無論那個屬性原本是如何實現的。關於屬性觀察器的更多內容,請看屬性觀察器。
注意:
你不可以爲繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置的,所以,爲它們提供willSet
或didSet
實現是不恰當。此外還要注意,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,並且你已經爲那個屬性提供了定製的 setter,那麼你在 setter 中就可以觀察到任何值變化了。
下面的例子定義了一個新類叫AutomaticCar
,它是Car
的子類。AutomaticCar
表示自動擋汽車,它可以根據當前的速度自動選擇合適的擋位。AutomaticCar
也提供了定製的description
方法,可以輸出當前擋位。
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)"
}
}
當你設置AutomaticCar
的speed
屬性,屬性的didSet
觀察器就會自動地設置gear
屬性,爲新的速度選擇一個合適的擋位。具體來說就是,屬性觀察器將新的速度值除以10,然後向下取得最接近的整數值,最後加1來得到檔位gear
的值。例如,速度爲10.0時,擋位爲1;速度爲35.0時,擋位爲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