鍍金池/ 教程/ iOS/ 繼承
特性(Attributes)
Access Control 權(quán)限控制的黑與白
基本運算符(Basic Operators)
基礎(chǔ)部分(The Basics)
閉包(Closures)
擴展
泛型參數(shù)(Generic Parameters and Arguments)
訪問控制和 protected
語句(Statements)
模式(Patterns)
WWDC 里面的那個“大炮打氣球”
關(guān)于語言參考(About the Language Reference)
語法總結(jié)(Summary of the Grammar)
嵌套類型
類型(Types)
Swift 初見(A Swift Tour)
泛型
枚舉(Enumerations)
高級運算符
繼承
析構(gòu)過程
關(guān)于 Swift(About Swift)
訪問控制
類和結(jié)構(gòu)體
內(nèi)存安全
Swift 與 C 語言指針友好合作
協(xié)議
屬性(Properties)
可選類型完美解決占位問題
錯誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動引用計數(shù)
Swift 里的值類型與引用類型
表達式(Expressions)
Swift 文檔修訂歷史
造個類型不是夢-白話 Swift 類型創(chuàng)建
歡迎使用 Swift
詞法結(jié)構(gòu)(Lexical Structure)
集合類型(Collection Types)
下標
方法(Methods)
可選鏈式調(diào)用
版本兼容性
類型轉(zhuǎn)換
構(gòu)造過程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

繼承


1.0 翻譯:Hawstein 校對:menlongsheng

2.0,2.1 翻譯+校對:shanks

2.2 校對:SketchK 2016-05-13

3.0.1,shanks,2016-11-13

4.0 校對:kemchenj 2017-09-21

4.1 翻譯+校對:mylittleswift

本頁包含內(nèi)容:

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

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

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

定義一個基類

不繼承于其它類的類,稱之為基類。

注意

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

下面的例子定義了一個叫 Vehicle 的基類。這個基類聲明了一個名為 currentSpeed,默認值是 0.0 的存儲屬性(屬性類型推斷為 Double)。currentSpeed 屬性的值被一個 String 類型的只讀計算型屬性 description 使用,用來創(chuàng)建車輛的描述。

Vehicle 基類也定義了一個名為 makeNoise 的方法。這個方法實際上不為 Vehicle 實例做任何事,但之后將會被 Vehicle 的子類定制:

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // 什么也不做-因為車輛不一定會有噪音
    }
}

您可以用初始化語法創(chuàng)建一個 Vehicle 的新實例,即類名后面跟一個空括號:

let someVehicle = Vehicle()

現(xiàn)在已經(jīng)創(chuàng)建了一個 Vehicle 的新實例,你可以訪問它的 description 屬性來打印車輛的當前速度:

print("Vehicle: \(someVehicle.description)")
// 打印 "Vehicle: traveling at 0.0 miles per hour"

Vehicle 類定義了一個通用特性的車輛類,實際上沒什么用處。為了讓它變得更加有用,需要完善它從而能夠描述一個更加具體類型的車輛。

子類生成

子類生成指的是在一個已有類的基礎(chǔ)上創(chuàng)建一個新的類。子類繼承超類的特性,并且可以進一步完善。你還可以為子類添加新的特性。

為了指明某個類的超類,將超類名寫在子類名的后面,用冒號分隔:

class SomeClass: SomeSuperclass {
    // 這里是子類的定義
}

下一個例子,定義一個叫 Bicycle 的子類,繼承成父類 Vehicle

class Bicycle: Vehicle {
    var hasBasket = false
}

新的 Bicycle 類自動獲得 Vehicle 類的所有特性,比如 currentSpeeddescription 屬性,還有它的 makeNoise() 方法。

除了它所繼承的特性,Bicycle 類還定義了一個默認值為 false 的存儲型屬性 hasBasket(屬性推斷為 Bool)。

默認情況下,你創(chuàng)建任何新的 Bicycle 實例將不會有一個籃子(即 hasBasket 屬性默認為 false),創(chuàng)建該實例之后,你可以為特定的 Bicycle 實例設(shè)置 hasBasket 屬性為 ture

let bicycle = Bicycle()
bicycle.hasBasket = true

你還可以修改 Bicycle 實例所繼承的 currentSpeed 屬性,和查詢實例所繼承的 description 屬性:

bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// 打印 "Bicycle: traveling at 15.0 miles per hour"

子類還可以繼續(xù)被其它類繼承,下面的示例為 Bicycle 創(chuàng)建了一個名為 Tandem(雙人自行車)的子類:

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

TandemBicycle 繼承了所有的屬性與方法,這又使它同時繼承了 Vehicle 的所有屬性與方法。Tandem 也增加了一個新的叫做 currentNumberOfPassengers 的存儲型屬性,默認值為 0

如果你創(chuàng)建了一個 Tandem 的實例,你可以使用它所有的新屬性和繼承的屬性,還能查詢從 Vehicle 繼承來的只讀屬性 description

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"

重寫

子類可以為繼承來的實例方法,類方法,實例屬性,或下標提供自己定制的實現(xiàn)。我們把這種行為叫重寫。

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

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

訪問超類的方法,屬性及下標

當你在子類中重寫超類的方法,屬性或下標時,有時在你的重寫版本中使用已經(jīng)存在的超類實現(xiàn)會大有裨益。比如,你可以完善已有實現(xiàn)的行為,或在一個繼承來的變量中存儲一個修改過的值。

在合適的地方,你可以通過使用 super 前綴來訪問超類版本的方法,屬性或下標:

  • 在方法 someMethod() 的重寫實現(xiàn)中,可以通過 super.someMethod() 來調(diào)用超類版本的 someMethod() 方法。
  • 在屬性 someProperty 的 getter 或 setter 的重寫實現(xiàn)中,可以通過 super.someProperty 來訪問超類版本的 someProperty 屬性。
  • 在下標的重寫實現(xiàn)中,可以通過 super[someIndex] 來訪問超類版本中的相同下標。

重寫方法

在子類中,你可以重寫繼承來的實例方法或類方法,提供一個定制或替代的方法實現(xiàn)。

下面的例子定義了 Vehicle 的一個新的子類,叫 Train,它重寫了從 Vehicle 類繼承來的 makeNoise() 方法:

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}

如果你創(chuàng)建一個 Train 的新實例,并調(diào)用了它的 makeNoise() 方法,你就會發(fā)現(xiàn) Train 版本的方法被調(diào)用:

let train = Train()
train.makeNoise()
// 打印 "Choo Choo"

重寫屬性

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

重寫屬性的 Getters 和 Setters

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

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

注意

如果你在重寫屬性中提供了 setter,那么你也一定要提供 getter。如果你不想在重寫版本中的 getter 里修改繼承來的屬性值,你可以直接通過 super.someProperty 來返回繼承來的值,其中 someProperty 是你要重寫的屬性的名字。

以下的例子定義了一個新類,叫 Car,它是 Vehicle 的子類。這個類引入了一個新的存儲型屬性叫做 gear,默認值為整數(shù) 1。Car 類重寫了繼承自 Vehicledescription 屬性,提供包含當前檔位的自定義描述:

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)于當前檔位的信息。

如果你創(chuàng)建了 Car 的實例并且設(shè)置了它的 gearcurrentSpeed 屬性,你可以看到它的 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"

重寫屬性觀察器

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

注意

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

下面的例子定義了一個新類叫 AutomaticCar,它是 Car 的子類。AutomaticCar 表示自動擋汽車,它可以根據(jù)當前的速度自動選擇合適的擋位:

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

無論何時當你設(shè)置 AutomaticCarcurrentSpeed 屬性,屬性的 didSet 觀察器就會自動地設(shè)置 gear 屬性,為新的速度選擇一個合適的擋位。具體來說就是,屬性觀察器將新的速度值除以 10,然后向下取得最接近的整數(shù)值,最后加 1 來得到檔位 gear 的值。例如,速度為 35.0 時,擋位為 4

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// 打印 "AutomaticCar: traveling at 35.0 miles per hour in gear 4"

防止重寫

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

任何試圖對帶有 final 標記的方法、屬性或下標進行重寫,都會在編譯時會報錯。在類擴展中的方法,屬性或下標也可以在擴展的定義里標記為 final 的。

你可以通過在關(guān)鍵字 class 前添加 final 修飾符(final class)來將整個類標記為 final 的。這樣的類是不可被繼承的,試圖繼承這樣的類會導(dǎo)致編譯報錯。