Swift(十八)繼承

desk-wallpaper-2880x1620.jpg

一個類可以繼承(inherit)另一個類的方法(methods),屬性(property)和其它特性。當一個類繼承其它類時,繼承類叫子類(subclass),被繼承類叫超類(或父類,superclass)。在 Swift 中,繼承是區(qū)分「類」與其它類型的一個基本特征。

在 Swift 中,類可以調用和訪問超類的方法,屬性和附屬腳本(subscripts),并且可以重寫(override)這些方法,屬性和附屬腳本來優(yōu)化或修改它們的行為。Swift 會檢查你的重寫定義在超類中是否有匹配的定義,以此確保你的重寫行為是正確的。

可以為類中繼承來的屬性添加屬性觀察器(property observer),這樣一來,當屬性值改變時,類就會被通知到??梢詾槿魏螌傩蕴砑訉傩杂^察器,無論它原本被定義為存儲型屬性(stored property)還是計算型屬性(computed property)。

注意:Swift 中的類并不是從一個通用的基類繼承而來。如果你不為你定義的類指定一個超類的話,這個類就自動成為基類。
與OC不同的是,OC的都都是繼承自NSObject

定義一個Vehicle類, 聲明兩個屬性, 并用構造器初始化屬性。

class Vehicle { 
    var numberOfWheels: Int 
    var maxPassengers: Int 
    func description() -> String { 
        return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" 
    } 
    init() { 
        numberOfWheels = 0 
        maxPassengers = 1 
    } 
} 

構造器用于創(chuàng)建某個類型的一個新實例。盡管構造器并不是方法,但在語法上,兩者很相似。構造器的工作是準備新實例以供使用,并確保實例中的所有屬性都擁有有效的初始化值。

構造器的最簡單形式就像一個沒有參數(shù)的實例方法,使用init關鍵字:

init() { 
    // 執(zhí)行構造過程 
} 

如果要創(chuàng)建一個Vehicle類的新實例,使用構造器語法調用上面的初始化器,即類名后面跟一個空的小括號:

let someVehicle = Vehicle() 

這個Vehicle類的構造器為任意的一輛車設置一些初始化屬性值(numberOfWheels = 0和maxPassengers = 1)。

Vehicle類定義了車輛的共同特性,但這個類本身并沒太大用處。為了使它更為實用,你需要進一步細化它來描述更具體的車輛。

子類(Subclassing)

繼承寫法

//class 子類: 父類
class SomeClass: SomeSuperclass { 
    // 類的定義 
} 

“定義一個新的類叫Bicycle,它繼承了Vehicle的特性”;

class Bicycle: Vehicle {
//此處要有override關鍵字, init方法在父類中已有實現(xiàn), 這與OC不同,在OC中,重寫方法不需要聲明任何關鍵字,只是重寫就可以了,
//編譯器會自己檢查該方法是否為父類所有, 如果有,則會自動覆蓋父類的方法,而使用子類重寫的方法
    override init() {
        super.init()
        numberOfWheels = 2
    }
}

Bicycle是Vehicle的子類,Vehicle是Bicycle的超類。新的Bicycle類自動獲得Vehicle類的特性,比如 maxPassengers和numberOfWheels屬性。你可以在子類中定制這些特性,或添加新的特性來更好地描述Bicycle類。

Bicycle類定義了一個構造器來設置它定制的特性(自行車只有2個輪子)。Bicycle的構造器調用了它父類Vehicle的構造器 super.init(),以此確保在Bicycle類試圖修改那些繼承來的屬性前Vehicle類已經(jīng)初始化過它們了。
子類不僅僅可以繼承屬性,還可以繼承方法

let bicycle = Bicycle() 
print("Bicycle: \(bicycle.description())") 
// Bicycle: 2 wheels; up to 1 passengers 

子類還可以繼續(xù)被其它類繼承:

class Tandem: Bicycle {
    override init() {
        super.init()
//修改繼承來的屬性
        maxPassengers = 4
    }
}

let tandem = Tandem()
print("Tandem: \(tandem.description())")
// Tandem: 2 wheels; up to 4 passengers

重寫(Overriding)

子類可以為繼承來的實例方法(instance method),類方法(class method),實例屬性(instance property),或附屬腳本(subscript)提供自己定制的實現(xiàn)(implementation)。我們把這種行為叫重寫(overriding)。

如果要重寫某個特性,你需要在重寫定義的前面加上override關鍵字。這么做,你就表明了你是想提供一個重寫版本,而非錯誤地提供了一個相同的定義。意外的重寫行為可能會導致不可預知的錯誤,任何缺少override關鍵字的重寫都會在編譯時被診斷為錯誤。

override關鍵字會提醒 Swift 編譯器去檢查該類的超類(或其中一個父類)是否有匹配重寫版本的聲明。這個檢查可以確保你的重寫定義是正確的。

在合適的地方,你可以通過使用super前綴來訪問超類版本的方法,屬性或附屬腳本:
在方法someMethod的重寫實現(xiàn)中,可以通過super.someMethod()來調用超類版本的someMethod方法。
在屬性someProperty的 getter 或 setter 的重寫實現(xiàn)中,可以通過super.someProperty來訪問超類版本的someProperty屬性。
在附屬腳本的重寫實現(xiàn)中,可以通過super[someIndex]來訪問超類版本中的相同附屬腳本。

重寫方法

class Car: Vehicle {
    var speed: Double = 0.0
    override init() {
        super.init()
        maxPassengers = 5
        numberOfWheels = 4
    }
    override func description() -> String {
        return super.description() + "; "
            + "traveling at \(speed) mph"
    }
}

let car = Car()
print("Car: \(car.description())")
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph

Car聲明了一個新的存儲型屬性speed,它是Double類型的,默認值是0.0,表示“時速是0英里”。Car有自己的初始化器,它將乘客的最大數(shù)量設為5,輪子數(shù)量設為4。

Car重寫了繼承來的description方法,它的聲明與Vehicle中的description方法一致,聲明前面加上了override關鍵字。

Car中的description方法并非完全自定義,而是通過super.description使用了超類Vehicle中的description方法,然后再追加一些額外的信息,比如汽車的當前速度。

重寫屬性

你可以重寫繼承來的實例屬性或類屬性,提供自己定制的getter和setter,或添加屬性觀察器使重寫的屬性觀察屬性值什么時候發(fā)生改變。

重寫屬性的Getters和Setters
你可以提供定制的 getter(或 setter)來重寫任意繼承來的屬性,無論繼承來的屬性是存儲型的還是計算型的屬性。子類并不知道繼承來的屬性是存儲型的還是計算型的,它只知道繼承來的屬性會有一個名字和類型。你在重寫一個屬性時,必需將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的。

你可以將一個繼承來的只讀屬性重寫為一個讀寫屬性,只需要你在重寫版本的屬性里提供 getter 和 setter 即可。但是,你不可以將一個繼承來的讀寫屬性重寫為一個只讀屬性。

注意:如果你在重寫屬性中提供了 setter,那么你也一定要提供 getter。如果你不想在重寫版本中的 getter 里修改繼承來的屬性值,你可以直接返回super.someProperty來返回繼承來的值。正如下面的SpeedLimitedCar的例子所示。
重寫繼承來的speed屬性來實現(xiàn)這個速度限制

class SpeedLimitedCar: Car {
    override var speed: Double  {
        get {
            return super.speed
        }
        set {
//當調用setter方法時,回去出父類和子類中速度最小的一個
            super.speed = min(newValue, 40.0)
        } 
    } 
}

如果你嘗試將SpeedLimitedCar實例的speed屬性設置為一個大于40mph的數(shù),然后打印description函數(shù)的輸出,你會發(fā)現(xiàn)速度被限制在40mph:

let limitedCar = SpeedLimitedCar()
limitedCar.speed = 60.0
print("SpeedLimitedCar: \(limitedCar.description())")

重寫屬性觀察器(Property Observer)

你可以在屬性重寫中為一個繼承來的屬性添加屬性觀察器。這樣一來,當繼承來的屬性值發(fā)生改變時,你就會被通知到,無論那個屬性原本是如何實現(xiàn)的。關于屬性觀察器的更多內容,請看屬性觀察器。

注意:你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置的,所以,為它們提供willSet或didSet實現(xiàn)是不恰當。此外還要注意,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,并且你已經(jīng)為那個屬性提供了定制的 setter,那么你在 setter 中就可以觀察到任何值變化了。

下面的例子定義了一個新類叫AutomaticCar,它是Car的子類。AutomaticCar表示自動擋汽車,它可以根據(jù)當前的速度自動選擇合適的擋位。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)"
    } 
}

let automatic = AutomaticCar()
automatic.speed = 35.0
print("AutomaticCar: \(automatic.description())")

防止重寫

你可以通過把方法,屬性或附屬腳本標記為final來防止它們被重寫,只需要在聲明關鍵字前加上@final特性即可。(例如:@final var, @final func, @final class func, 以及 @final subscript)

如果你重寫了final方法,屬性或附屬腳本,在編譯時會報錯。在擴展中,你添加到類里的方法,屬性或附屬腳本也可以在擴展的定義里標記為 final。

你可以通過在關鍵字class前添加@final特性(@final class)來將整個類標記為 final 的,這樣的類是不可被繼承的,否則會報編譯錯誤。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容