Swift-繼承

優質神經病發表於2018-07-27

一個類可以繼承另一個類的方法,屬性和其他特性。當一個類繼承其他類時,繼承的類叫子類,被繼承的類叫超類(或父類)。在Swift中,繼承是區分類與其他型別的一個基本特徵。只有類才能被繼承。

在Swift中,類可以呼叫和訪問超類的方法、屬性和下標,並且可以重新這些方法。屬性和下標來優化或修改它們的行為。Swift會檢查你的重寫定義在超類彙總是否有匹配的定義,以確保你的重寫行為是否正確。

可以為類中繼承來的屬性新增屬性觀察器,這樣一來,當屬性值被改變,類就會被通知到。可以為任何屬性新增屬性觀察器(類似OC的KVO),無論它原本是被定義為儲存型屬性還是計算型屬性。

本文內容包括:定義一個基類子類生成防止重寫


定義一個基類

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

下面的例子定義了一個叫Vehicle的基類。這個基類宣告瞭一個名為currentSpeed,預設值是0.0的儲存屬性(屬性型別推斷為Double)。currentSpeed屬性的值被一個String型別的只讀計算屬性description使用,用來建立車輛的描述。

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

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

您可以用初始化語法建立一個 Vehicle 的新例項,即類名後面跟一個空括號:

let someVehicle = Vehicle()
複製程式碼

現在已經建立了一個 Vehicle 的新例項,你可以訪問它的 description 屬性來列印車輛的當前速度:

print("Vehicle: \(someVehicle.description)")
// 列印 "Vehicle: traveling at 0.0 miles per hour"
複製程式碼

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

子類生成

子類生成指的是在一個已有的類的基礎上建立一個新的類。子類繼承超類的特性,並且可以進一步完善。你還可以為子類新增新的特性。

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

class SomeClass: SomeSuperclass {
    // 這裡是子類的定義
}
複製程式碼

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

class Bicycle: Vehicle {
    var hasBasket = false
}
複製程式碼

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

除了它所繼承的特性,Bicycle類還定義了一個新的預設值為false的儲存屬性hasBasket

預設情況下,建立任何新的Bicycle例項的hasBasket屬性預設都是false。建立該例項後,可以為特定的Bicycle例項設定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"
複製程式碼

子類還可以繼續被其他類繼承,下面的示例為Bicycle建立一個名為Tandem的子類:

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}
複製程式碼

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

如果建立一個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"
複製程式碼

重寫

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

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

override關鍵字會提醒Swift編譯器去檢查該類的超類是否有匹配重寫版本的宣告。這個檢查可以確保你的重寫定義是正確的。

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

當你在子類中重寫超類的方法,屬性或下標時,有時在你的重寫版本中使用已存在的超類實現會大有好處。比如可以完善已有實現的行為,或在一個繼承來的變數中儲存一個修改過的值。

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

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

重寫方法

在子類中,可以重寫繼承來的例項方法或類方法,提供一個定製或替代的方法實現。

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

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}
複製程式碼

如果你建立一個 Train 的新例項,並呼叫了它的 makeNoise() 方法,你就會發現 Train 版本的方法被呼叫:

let train = Train()
train.makeNoise()
// 列印 "Choo Choo"
複製程式碼

重寫屬性

可以重寫繼承來的例項屬性或型別屬性,提供自己定製的getter和setter方法,或新增屬性觀察器使重寫的屬性可以觀察屬性值什麼時候發生改變。

重寫屬性的Getters和Setters

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

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

以下的例子定義了一個新類,叫Car,它是Vehicle的子類。這個類引入了一個新的儲存型屬性叫做gear,預設值為整數1Car類重新了繼承自Vehicledescription屬性,提供包含當前黨委的自定義描述:

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

重新的description屬性首先要呼叫super.description返回Vehicle類的description屬性。之後,Car類版本的description在末尾增加了一些額外的文字來提供相關當前檔位的資訊。

如果你建立了Car的例項並且設定了它的gearcurrentSpeed屬性,你可以看到它的description返回了Car中的自定義描述:

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

重寫的 description 屬性首先要呼叫 super.description 返回 Vehicle 類的 description 屬性。之後,Car 類版本的 description 在末尾增加了一些額外的文字來提供關於當前檔位的資訊。

如果你建立了 Car 的例項並且設定了它的 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"
複製程式碼
重寫屬性觀察器

可以通過重寫屬性為一個繼承來的屬性新增屬性觀察器。這樣一來,當繼承來的屬性值發生改變時,你就會被通知,無論那個屬性原本是如何實現的。

不可以為繼承來的常量儲存型屬性或繼承來的制度計算型屬性新增屬性觀察器。這些屬性的值是不可以被設定的,所以他們提供的willSetdidSet實現是不恰當的。

此外還要注意,不可以同時提供重寫的setter和重寫的屬性觀察器。如果想觀察屬性值的變化,並且已經為那個屬性提供了定製的setter,那麼你在setter中就可以觀察到任何值變化了。

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

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

無論何時當你設定 AutomaticCarcurrentSpeed 屬性,屬性的 didSet 觀察器就會自動地設定 gear 屬性,為新的速度選擇一個合適的擋位。具體來說就是,屬性觀察器將新的速度值除以 10,然後向下取得最接近的整數值,最後加 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修飾符防止被重寫。如:final varfinal funcfinal class func,以及 final subscript

當新增關鍵字final後重新會編譯錯誤。如果在class前新增該關鍵字,那麼這個類將不能被繼承。

相關文章