繼承 - Inheritance

2018-12-06 14:50 更新

繼承(Inheritance)

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

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

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

定義一個(gè)基類(lèi)(Base class)

不繼承于其它類(lèi)的類(lèi),稱(chēng)之為基類(lèi)(base calss)。

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

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

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

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

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

    let someVehicle = Vehicle()

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

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

子類(lèi)生成(Subclassing)

子類(lèi)生成(Subclassing)指的是在一個(gè)已有類(lèi)的基礎(chǔ)上創(chuàng)建一個(gè)新的類(lèi)。子類(lèi)繼承超類(lèi)的特性,并且可以?xún)?yōu)化或改變它。你還可以為子類(lèi)添加新的特性。

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

    class SomeClass: SomeSuperclass {
        // 類(lèi)的定義
    }

下一個(gè)例子,定義一個(gè)更具體的車(chē)輛類(lèi)叫Bicycle。這個(gè)新類(lèi)是在 Vehicle類(lèi)的基礎(chǔ)上創(chuàng)建起來(lái)。因此你需要將Vehicle類(lèi)放在 Bicycle類(lèi)后面,用冒號(hào)分隔。

我們可以將這讀作:

“定義一個(gè)新的類(lèi)叫Bicycle,它繼承了Vehicle的特性”;

    class Bicycle: Vehicle {
        var hasBasket = false
    }

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

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

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

    let bicycle = Bicycle()
    bicycle.hasBasket = true

你還可以修改Bicycle實(shí)例所繼承的currentSpeed屬性,和查詢(xún)實(shí)例所繼承的description屬性:

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

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

    class Tandem: Bicycle {
        var currentNumberOfPassengers = 0
    }

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

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

    let tandem = Tandem()
    tandem.hasBasket = true
    tandem.currentNumberOfPassengers = 2
    tandem.currentSpeed = 22.0
    println("Tandem: \(tandem.description)")
    // Tandem: traveling at 22.0 miles per hour

重寫(xiě)(Overriding)

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

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

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

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

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

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

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

重寫(xiě)方法

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

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

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

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

    let train = Train()
    train.makeNoise()
    // prints "Choo Choo"

重寫(xiě)屬性

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

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

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

你可以將一個(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è)新類(lèi),叫Car,它是Vehicle的子類(lèi)。這個(gè)類(lèi)引入了一個(gè)新的存儲(chǔ)型屬性叫做gear,默認(rèn)為整數(shù)1。Car類(lèi)重寫(xiě)了繼承自Vehicle的description屬性,提供自定義的,包含當(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類(lèi)的description屬性。之后,Car類(lèi)版本的description在末尾增加了一些額外的文本來(lái)提供關(guān)于當(dāng)前檔位的信息。

如果你創(chuàng)建了Car的實(shí)例并且設(shè)置了它的gearcurrentSpeed屬性,你可以看到它的description返回了Car中定義的description

    let car = Car()
    car.currentSpeed = 25.0
    car.gear = 3
    println("Car: \(car.description)")
    // Car: traveling at 25.0 miles per hour in gear 3

重寫(xiě)屬性觀察器(Property Observer)

你可以在屬性重寫(xiě)中為一個(gè)繼承來(lái)的屬性添加屬性觀察器。這樣一來(lái),當(dāng)繼承來(lái)的屬性值發(fā)生改變時(shí),你就會(huì)被通知到,無(wú)論那個(gè)屬性原本是如何實(shí)現(xiàn)的。關(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è)新類(lèi)叫AutomaticCar,它是Car的子類(lèi)。AutomaticCar表示自動(dòng)擋汽車(chē),它可以根據(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的值。例如,速度為10.0時(shí),擋位為1;速度為35.0時(shí),擋位為4:

    let automatic = AutomaticCar()
    automatic.currentSpeed = 35.0
    println("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

如果你重寫(xiě)了final方法,屬性或下標(biāo)腳本,在編譯時(shí)會(huì)報(bào)錯(cuò)。在擴(kuò)展中,你添加到類(lèi)里的方法,屬性或下標(biāo)腳本也可以在擴(kuò)展的定義里標(biāo)記為 final。

你可以通過(guò)在關(guān)鍵字class前添加final特性(final class)來(lái)將整個(gè)類(lèi)標(biāo)記為 final 的,這樣的類(lèi)是不可被繼承的,否則會(huì)報(bào)編譯錯(cuò)誤。

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)