標籤:des style blog java 使用 io strong 2014
一個類能夠繼承(inherit)還有一個類的方法(methods),屬性(property)和其他特性。當一個類繼承其他類時,繼承類叫子類(subclass),被繼承類叫超類(或父類,superclass)。在 Swift 中,繼承是區分「類」與其他類型的一個基本特徵。
在 Swift 中,類能夠調用和訪問超類的方法,屬性和附屬指令碼(subscripts),而且能夠重寫(override)這些方法,屬性和附屬指令碼來最佳化或改動它們的行為。Swift 會檢查你的重寫定義在超類中是否有匹配的定義,以此確保你的重寫行為是正確的。
能夠為類中繼承來的屬性加入屬性觀察器(property observer),這樣一來,當屬性值改變時,類就會被通知到。能夠為不論什麼屬性加入屬性觀察器,不管它原本被定義為儲存型屬性(stored property)還是計算型屬性(computed property)。
定義一個基類(Base class)
不繼承於其他類的類,稱之為基類(base calss)。
注意:
Swift 中的類並非從一個通用的基類繼承而來。假設你不為你定義的類指定一個超類的話,這個類就自己主動成為基類。
以下的範例定義了一個叫Vehicle的基類。這個基類聲明了兩個對全部車輛都通用的屬性(numberOfWheels和maxPassengers)。這些屬性在description方法中使用,這種方法返回一個String類型的,對車輛特徵的描寫敘述:
class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { return "\(numberOfWheels) wheels; up to \(maxPassengers)passengers" } init() { numberOfWheels = 0 maxPassengers = 1 }}
Vehicle類定義了構造器(initializer)來設定屬性的值。構造器會在構造過程一節中具體介紹,這裡我們做一下簡介,以便於解說子類中繼承來的屬性怎樣被改動。
構造器用於建立某個類型的一個新執行個體。雖然構造器並非方法,但在文法上,兩者非常相似。構造器的工作是準備新執行個體以供使用,並確保執行個體中的全部屬性都擁有有效初始化值。
構造器的最簡單形式就像一個沒有參數的執行個體方法,使用initkeyword:
init() { // 運行構造過程}
假設要建立一個Vehicle類的新執行個體,使用構造器文法調用上面的初始化器,即類名後面跟一個空的小括弧:
let someVehicle = Vehicle()
這個Vehicle類的構造器為隨意的一輛車設定一些初始化屬性值(numberOfWheels = 0和maxPassengers = 1)。
Vehicle類定義了車輛的共同特性,但這個類本身並沒太大用處。為了使它更為有用,你須要進一步細化它來描寫敘述更詳細的車輛。
子類產生(Subclassing)
子類產生(Subclassing)指的是在一個已有類的基礎上建立一個新的類。子類繼承超類的特性,而且能夠最佳化或改變它。你還能夠為子類加入新的特性。
為了指明某個類的超類,將超類名寫在子類名的後面,用冒號分隔:
class SomeClass: SomeSuperclass { // 類的定義}
下一個範例,定義一個更詳細的車輛類叫Bicycle。這個新類是在 Vehicle類的基礎上建立起來。因此你須要將Vehicle類放在 Bicycle類後面,用冒號分隔。
我們能夠將這讀作:
“定義一個新的類叫Bicycle,它繼承了Vehicle的特性”;
class Bicycle: Vehicle { init() { super.init() numberOfWheels = 2 }}
Bicycle是Vehicle的子類,Vehicle是Bicycle的超類。新的Bicycle類自己主動獲得Vehicle類的特性,比方maxPassengers和numberOfWheels屬性。你能夠在子類中定製這些特性,或加入新的特性來更好地描寫敘述Bicycle類。
Bicycle類定義了一個構造器來設定它定製的特性(單車僅僅有2個輪子)。Bicycle的構造器調用了它父類Vehicle的構造器 super.init(),以此確保在Bicycle類試圖改動那些繼承來的屬性前Vehicle類已經初始化過它們了。
注意:
不像 Objective-C,在 Swift 中,初始化器預設是不繼承的,見初始化器的繼承與重寫
Vehicle類中maxPassengers的預設值對單車來說已經是正確的,因此在Bicycle的構造器中並沒有改變它。而numberOfWheels原來的值對單車來說是不對的,因此在初始化器中將它更改為 2。
Bicycle不僅能夠繼承Vehicle的屬性,還能夠繼承它的方法。假設你建立了一個Bicycle類的執行個體,你就能夠調用它繼承來的description方法,而且能夠看到,它輸出的屬性值已經發生了變化:
let bicycle = Bicycle()println("Bicycle:\(bicycle.description())")// Bicycle: 2 wheels; up to 1 passengers
子類還能夠繼續被其他類繼承:
class Tandem: Bicycle { init() { super.init() maxPassengers = 2 }}
上面的範例建立了Bicycle的一個子類:雙人單車(tandem)。Tandem從Bicycle繼承了兩個屬性,而這兩個屬性是Bicycle從Vehicle繼承而來的。Tandem並不改動輪子的數量,由於它仍是一輛單車,有 2 個輪子。但它須要改動maxPassengers的值,由於雙人單車能夠坐兩個人。
注意:
子類僅僅同意改動從超類繼承來的變數屬性,而不能改動繼承來的常量屬性。
建立一個Tandem類的執行個體,列印它的描寫敘述,就可以看到它的屬性已被更新:
let tandem = Tandem()println("Tandem:\(tandem.description())")// Tandem: 2 wheels; up to 2 passengers
注意,Tandem類也繼承了description方法。一個類的執行個體方法會被這個類的全部子類繼承。
重寫(Overriding)
子類能夠為繼承來的執行個體方法(instance method),類方法(class method),執行個體屬性(instance property),或附屬指令碼(subscript)提供自己定製的實現(implementation)。我們把這樣的行為叫重寫(overriding)。
假設要重寫某個特性,你須要在重寫定義的前面加上overridekeyword。這麼做,你就表明了你是想提供一個重寫版本號碼,而非錯誤地提供了一個同樣的定義。意外的重寫行為可能會導致不可預知的錯誤,不論什麼缺少overridekeyword的重寫都會在編譯時間被診斷為錯誤。
overridekeyword會提醒 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方法一致,聲明前面加上了overridekeyword。
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.0println("SpeedLimitedCar:\(limitedCar.description())")// SpeedLimitedCar: 4 wheels; up to 5passengers; 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.0println("AutomaticCar:\(automatic.description())")// AutomaticCar: 4 wheels; up to 5passengers; traveling at 35.0 mph in gear 4
防止重寫
你能夠通過把方法,屬性或附屬指令碼標記為final來防止它們被重寫,僅僅須要在聲明keyword前加上@final特性就可以。(比如:@final var, @final func, @final class func, 以及 @finalsubscript)
假設你重寫了final方法,屬性或附屬指令碼,在編譯時間會報錯。在擴充中,你加入到類裡的方法,屬性或附屬指令碼也能夠在擴充的定義裡標記為 final。
你能夠通過在keywordclass前加入@final特性(@final class)來將整個類標記為 final 的,這種類是不可被繼承的,否則會報編譯錯誤。