> 1.0 翻譯:[Hawstein](https://github.com/Hawstein)?校對:[menlongsheng](https://github.com/menlongsheng)
>
> 2.0 翻譯+校對:[shanksyang](https://github.com/shanksyang)
本頁包含內容:
[TOC=2]
一個類可以_繼承(inherit)_另一個類的方法(methods),屬性(properties)和其它特性。當一個類繼承其它類時,繼承類叫_子類(subclass)_,被繼承類叫_超類(或父類,superclass)_。在 Swift 中,繼承是區分「類」與其它類型的一個基本特征。
在 Swift 中,類可以調用和訪問超類的方法,屬性和下標腳本(subscripts),并且可以重寫(override)這些方法,屬性和下標腳本來優化或修改它們的行為。Swift 會檢查你的重寫定義在超類中是否有匹配的定義,以此確保你的重寫行為是正確的。
可以為類中繼承來的屬性添加屬性觀察器(property observers),這樣一來,當屬性值改變時,類就會被通知到。可以為任何屬性添加屬性觀察器,無論它原本被定義為存儲型屬性(stored property)還是計算型屬性(computed property)。
## 定義一個基類(Base class)
不繼承于其它類的類,稱之為_基類(base calss)_。
> 注意:
> 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`類定義了一個通用特性的車輛類,實際上沒什么用處。為了讓它變得更加有用,需要改進它能夠描述一個更加具體的車輛類。
## 子類生成(Subclassing)
_子類生成(Subclassing)_指的是在一個已有類的基礎上創建一個新的類。子類繼承超類的特性,并且可以優化或改變它。你還可以為子類添加新的特性。
為了指明某個類的超類,將超類名寫在子類名的后面,用冒號分隔:
~~~
class SomeClass: SomeSuperclass {
// 類的定義
}
~~~
下一個例子,定義一個叫`Bicycle`的子類,繼承成父類`Vehicle`
~~~
class Bicycle: Vehicle {
var hasBasket = false
}
~~~
新的`Bicycle`類自動獲得`Vehicle`類的所有特性,比如?`currentSpeed`和`description`屬性,還有它的`makeNoise`方法。
除了它所繼承的特性,`Bicycle`類還定義了一個默認值為`false`的存儲型屬性`hasBasket`(屬性推斷為`Bool`)。
默認情況下,你創建任何新的`Bicycle`實例將不會有一個籃子,創建該實例之后,你可以為特定的`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
}
~~~
`Tandem`從`Bicycle`繼承了所有的屬性與方法,這又使它同時繼承了`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
~~~
## 重寫(Overriding)
子類可以為繼承來的實例方法(instance method),類方法(class method),實例屬性(instance property),或下標腳本(subscript)提供自己定制的實現(implementation)。我們把這種行為叫_重寫(overriding)_。
如果要重寫某個特性,你需要在重寫定義的前面加上`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()
// prints "Choo Choo"
~~~
### 重寫屬性
你可以重寫繼承來的實例屬性或類屬性,提供自己定制的getter和setter,或添加屬性觀察器使重寫的屬性可以觀察屬性值什么時候發生改變。
#### 重寫屬性的Getters和Setters
你可以提供定制的 getter(或 setter)來重寫任意繼承來的屬性,無論繼承來的屬性是存儲型的還是計算型的屬性。子類并不知道繼承來的屬性是存儲型的還是計算型的,它只知道繼承來的屬性會有一個名字和類型。你在重寫一個屬性時,必需將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的。
你可以將一個繼承來的只讀屬性重寫為一個讀寫屬性,只需要你在重寫版本的屬性里提供 getter 和 setter 即可。但是,你不可以將一個繼承來的讀寫屬性重寫為一個只讀屬性。
> 注意:
> 如果你在重寫屬性中提供了 setter,那么你也一定要提供 getter。如果你不想在重寫版本中的 getter 里修改繼承來的屬性值,你可以直接通過`super.someProperty`來返回繼承來的值,其中`someProperty`是你要重寫的屬性的名字。
以下的例子定義了一個新類,叫`Car`,它是`Vehicle`的子類。這個類引入了一個新的存儲型屬性叫做`gear`,默認為整數1。`Car`類重寫了繼承自`Vehicle`的description屬性,提供自定義的,包含當前檔位的描述:
~~~
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
~~~
重寫的`description`屬性,首先要調用`super.description`返回`Vehicle`類的`description`屬性。之后,`Car`類版本的`description`在末尾增加了一些額外的文本來提供關于當前檔位的信息。
如果你創建了`Car`的實例并且設置了它的`gear`和`currentSpeed`屬性,你可以看到它的`description`返回了`Car`中定義的`description`:
~~~
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
~~~
#### 重寫屬性觀察器(Property Observer)
你可以在屬性重寫中為一個繼承來的屬性添加屬性觀察器。這樣一來,當繼承來的屬性值發生改變時,你就會被通知到,無論那個屬性原本是如何實現的。關于屬性觀察器的更多內容,請看[屬性觀察器](http://wiki.jikexueyuan.com/project/swift/chapter2/10_Properties.html#property_observers)。
> 注意:
> 你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置的,所以,為它們提供`willSet`或`didSet`實現是不恰當。此外還要注意,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,并且你已經為那個屬性提供了定制的 setter,那么你在 setter 中就可以觀察到任何值變化了。
下面的例子定義了一個新類叫`AutomaticCar`,它是`Car`的子類。`AutomaticCar`表示自動擋汽車,它可以根據當前的速度自動選擇合適的擋位:
~~~
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
~~~
當你設置`AutomaticCar`的`currentSpeed`屬性,屬性的`didSet`觀察器就會自動地設置`gear`屬性,為新的速度選擇一個合適的擋位。具體來說就是,屬性觀察器將新的速度值除以10,然后向下取得最接近的整數值,最后加1來得到檔位`gear`的值。例如,速度為10.0時,擋位為1;速度為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`特性即可。(例如:`final var`,?`final func`,?`final class func`, 以及?`final subscript`)
如果你重寫了`final`方法,屬性或下標腳本,在編譯時會報錯。在類擴展中的方法,屬性或下標腳本也可以在擴展的定義里標記為 final。
你可以通過在關鍵字`class`前添加`final`特性(`final class`)來將整個類標記為 final 的,這樣的類是不可被繼承的,任何子類試圖繼承此類時,在編譯時會報錯。
- 介紹
- 歡迎使用 Swift
- 關于 Swift
- Swift 初見
- Swift 版本歷史記錄
- Swift1.0 發布內容
- Swift 教程
- 基礎部分
- 基本運算符
- 字符串和字符
- 集合類型
- 控制流
- 函數
- 閉包
- 枚舉
- 類和結構體
- 屬性
- 方法
- 下標腳本
- 繼承
- 構造過程
- 析構過程
- 自動引用計數
- 可選鏈
- 錯誤處理
- 類型轉換
- 嵌套類型
- 擴展
- 協議
- 泛型
- 權限控制
- 高級操作符
- 語言參考
- 關于語言參考
- 詞法結構
- 類型
- 表達式
- 語句
- 聲明
- 特性
- 模式
- 泛型參數
- 語法總結
- 蘋果官方Blog官方翻譯
- Access Control 權限控制的黑與白
- 造個類型不是夢-白話Swift類型創建
- WWDC里面的那個“大炮打氣球”
- Swift與C語言指針友好合作
- 引用類型和值類型的恩怨
- 訪問控制和Protected
- 可選類型完美解決占位問題