## POLYMORPHISM AND TYPE CASTING
Polymorphism 譯為多態,指的是在類的繼承中,子類會繼承父類的屬性、方法,多態即指子類可以擁有父類或自身定義的兩種行為,你可以為其選擇:
~~~
class Album {
var name: String
init(name: String) {
self.name = name
}
}
class StudioAlbum: Album {
var studio: String
init(name: String, studio: String) {
self.studio = studio
super.init(name: name)
}
}
class LiveAlbum: Album {
var location: String
init(name: String, location: String) {
self.location = location
super.init(name: name) //子類調用父類方法,實現父類的行為
}
}
~~~
當子類想要實現自己的行為時,可以通過?`override`關鍵字 重寫 父類方法,如此將會實現子類自己定義的方法行為:
~~~
class LiveAlbum: Album {
var location: String
init(name: String, location: String) {
self.location = location
super.init(name: name)
}
override func getPerformance() -> String {
return "The live album \(name) sold lots"
}
}
~~~
總而言之,一個對象可以同時實現自己類的行為和其父類的行為,這稱為「多態」。
### Converting types with type casting
這種情況時有發生:你有一個明確聲明的對象,但你知道它其實是另一種類型(比如上面的繼承類StudioAlbum 和 LiveAlbum 被當做 Album 保存在數組中,因為它們繼承于 Album 所以是允許的),當需要調用方法時,Swift 可能不知道它的真實類型而無法編譯,解決辦法是 type casting,即類型轉換,可以將一個對象的類型轉為另一種類型:
~~~
for album in allAlbums {
print(album.getPerformance())
} //根據上面代碼塊的內容
~~~
`allAlbums`?數組擁有三個類型為?`Album`?的對象,但是其中兩個我們知道是`StudioAlbum`?和?`LiveAlbum`,但是 Swift 卻不知道,如果你想執行`print(album.studio)`?則無法編譯,因為只有?`StudioAlbum`擁有那個屬性。
Type casting 有三種形式,但常見的只有兩種:`as?`?和?`as!`,分別是可選向下轉型以及強制向下轉型,前者會返回一個轉型后的可選值(optional value),若轉型失敗會返回nil;當你確定可以轉型成功時使用后者,如果轉型失敗可能導致應用崩潰:?*P.S.「轉型」并不是指真的改變實例或它的值,而只是告訴 Swift 把這個對象看做某個類的實例。*
~~~
for album in allAlbums {
let studioAlbum = album as? StudioAlbum
}
~~~
studioAlbum 變量將會擁有一個StudioAlbum?類型數據或是nil,這經常與`if let`配合使用來自動解包 optional 值:
~~~
for album in allAlbums {
print(album.getPerformance())
if let studioAlbum = album as? StudioAlbum {
print(studioAlbum.studio)
} else if let liveAlbum = album as? LiveAlbum {
print(liveAlbum.location)
}
}
~~~
遍歷 allAlums 數組內的對象,并判斷它們是否為特定子類,如果是,調用子類的方法/屬性。
強制向下轉型(forced downcasting)就相當于轉型并強制拆包,返回的是一個非 optional 值,可以直接使用:
~~~
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios")
var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio")
var allAlbums: [Album] = [taylorSwift, fearless]
for album in allAlbums {
let studioAlbum = album as! StudioAlbum
print(studioAlbum.studio)
//便利數組時如果數組內有 liveAlbum 類的實例就會 crash,因為使用了強制轉型
//所以為了不 crash,只存放 `StudioAlbum` 實例在數組中
}
~~~
Swfit 也允許將轉型寫在數組遍歷層,在數組便利初始就將數據轉型,如此更有效率:
~~~
for album in allAlbums as! [StudioAlbum] {
print(album.studio) //相當于省去了 let studioAlbum = album as! StudioAlbum
}
~~~
但這么用必須得確保數組中所有實例都是 StudioAlbum 類型,否則會crash。
可選向下轉型(optional downcasting)也可以這么用,但因為這樣做有可能提供給「遍歷」一個nil,所以需要用 ??(nil coalescing operator) 來確保提供給 loop 一個值:
~~~
for album in allAlbums as? [LiveAlbum] ?? [LiveAlbum]()) {
print(album.location)
}
~~~
這樣相當于:嘗試將對象從?`allAlbums`?轉為?`LiveAlbum`?類型,如果失敗,就創建一個空的?`LiveAlbum`?對象來替代,這相當于啥也沒做。這有可能會用得上,不過最好不這么做。