這里的教程為Swift官方教程中文版。

繼承

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

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

可以為類中繼承來(lái)的屬性添加屬性觀察器,這樣一來(lái),當(dāng)屬性值改變時(shí),類就會(huì)被通知到??梢詾槿魏螌傩蕴砑訉傩杂^察器,無(wú)論它原本被定義為存儲(chǔ)型屬性還是計(jì)算型屬性。

定義一個(gè)基類

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

注意

Swift 中的類并不是從一個(gè)通用的基類繼承而來(lái)的。如果你不為自己定義的類指定一個(gè)超類的話,這個(gè)類就會(huì)自動(dòng)成為基類。

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

Vehicle 基類還定義了一個(gè)名為 makeNoise 的方法。這個(gè)方法實(shí)際上不為 Vehicle 實(shí)例做任何事,但之后將會(huì)被 Vehicle 的子類定制:

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // 什么也不做——因?yàn)檐囕v不一定會(huì)有噪音
    }
}

可以用初始化語(yǔ)法創(chuàng)建一個(gè) Vehicle 的新實(shí)例,即類名后面跟一個(gè)空括號(hào):

let someVehicle = Vehicle()

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

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

Vehicle 類定義了一個(gè)具有通用特性的車輛類,但實(shí)際上對(duì)于它本身來(lái)說(shuō)沒(méi)什么用處。為了讓它變得更加有用,還需要進(jìn)一步完善它,從而能夠描述一個(gè)具體類型的車輛。

子類生成

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

為了指明某個(gè)類的超類,將超類名寫(xiě)在子類名的后面,用冒號(hào)分隔:

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

下一個(gè)例子,定義了一個(gè)叫 Bicycle 的子類,繼承自父類 Vehicle

class Bicycle: Vehicle {
    var hasBasket = false
}

新的 Bicycle 類自動(dòng)繼承 Vehicle 類的所有特性,比如 currentSpeeddescription 屬性,還有 makeNoise() 方法。

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

默認(rèn)情況下,你創(chuàng)建的所有新的 Bicycle 實(shí)例不會(huì)有一個(gè)籃子(即 hasBasket 屬性默認(rèn)為 false)。創(chuàng)建該實(shí)例之后,你可以為 Bicycle 實(shí)例設(shè)置 hasBasket 屬性為 ture

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”

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

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

TandemBicycle 繼承了所有的屬性與方法,這又使它同時(shí)繼承了 Vehicle 的所有屬性與方法。Tandem 也增加了一個(gè)新的叫做 currentNumberOfPassengers 的存儲(chǔ)型屬性,默認(rèn)值為 0

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

let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// 打?。骸癟andem: traveling at 22.0 miles per hour”

重寫(xiě)

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

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

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

訪問(wèn)超類的方法,屬性及下標(biāo)

當(dāng)你在子類中重寫(xiě)超類的方法,屬性或下標(biāo)時(shí),有時(shí)在你的重寫(xiě)版本中使用已經(jīng)存在的超類實(shí)現(xiàn)會(huì)大有裨益。比如,你可以完善已有實(shí)現(xiàn)的行為,或在一個(gè)繼承來(lái)的變量中存儲(chǔ)一個(gè)修改過(guò)的值。

在合適的地方,你可以通過(guò)使用 super 前綴來(lái)訪問(wèn)超類版本的方法,屬性或下標(biāo):

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

重寫(xiě)方法

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

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

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

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

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

重寫(xiě)屬性

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

重寫(xiě)屬性的 Getters 和 Setters

你可以提供定制的 getter(或 setter)來(lái)重寫(xiě)任何一個(gè)繼承來(lái)的屬性,無(wú)論這個(gè)屬性是存儲(chǔ)型還是計(jì)算型屬性。子類并不知道繼承來(lái)的屬性是存儲(chǔ)型的還是計(jì)算型的,它只知道繼承來(lái)的屬性會(huì)有一個(gè)名字和類型。你在重寫(xiě)一個(gè)屬性時(shí),必須將它的名字和類型都寫(xiě)出來(lái)。這樣才能使編譯器去檢查你重寫(xiě)的屬性是與超類中同名同類型的屬性相匹配的。

你可以將一個(gè)繼承來(lái)的只讀屬性重寫(xiě)為一個(gè)讀寫(xiě)屬性,只需要在重寫(xiě)版本的屬性里提供 getter 和 setter 即可。但是,你不可以將一個(gè)繼承來(lái)的讀寫(xiě)屬性重寫(xiě)為一個(gè)只讀屬性。

注意

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

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

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

重寫(xiě)的 description 屬性首先要調(diào)用 super.description 返回 Vehicle 類的 description 屬性。之后,Car 類版本的 description 在末尾增加了一些額外的文本來(lái)提供關(guān)于當(dāng)前檔位的信息。

如果你創(chuàng)建了 Car 的實(shí)例并且設(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”

重寫(xiě)屬性觀察器

你可以通過(guò)重寫(xiě)屬性為一個(gè)繼承來(lái)的屬性添加屬性觀察器。這樣一來(lái),無(wú)論被繼承屬性原本是如何實(shí)現(xiàn)的,當(dāng)其屬性值發(fā)生改變時(shí),你就會(huì)被通知到。關(guān)于屬性觀察器的更多內(nèi)容,請(qǐng)看 屬性觀察器。

注意

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

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

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

當(dāng)你設(shè)置 AutomaticCarcurrentSpeed 屬性,屬性的 didSet 觀察器就會(huì)自動(dòng)地設(shè)置 gear 屬性,為新的速度選擇一個(gè)合適的檔位。具體來(lái)說(shuō)就是,屬性觀察器將新的速度值除以 10,然后向下取得最接近的整數(shù)值,最后加 1 來(lái)得到檔位 gear 的值。例如,速度為 35.0 時(shí),檔位為 4

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

防止重寫(xiě)

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

任何試圖對(duì)帶有 final 標(biāo)記的方法、屬性或下標(biāo)進(jìn)行重寫(xiě)的代碼,都會(huì)在編譯時(shí)會(huì)報(bào)錯(cuò)。在類擴(kuò)展中的方法,屬性或下標(biāo)也可以在擴(kuò)展的定義里標(biāo)記為 final。

可以通過(guò)在關(guān)鍵字 class 前添加 final 修飾符(final class)來(lái)將整個(gè)類標(biāo)記為 final 。這樣的類是不可被繼承的,試圖繼承這樣的類會(huì)導(dǎo)致編譯報(bào)錯(cuò)。

? 下標(biāo) 構(gòu)造過(guò)程 ?
?