- 類型轉(zhuǎn)換可以判斷實(shí)例的類型昭抒,也可以將實(shí)例看做是其父類或者子類的實(shí)例蚀浆。
- 類型轉(zhuǎn)換在 Swift 中使用 is 和 as 操作符實(shí)現(xiàn)。
- 也可以用它來檢查一個(gè)類型是否遵循了某個(gè)協(xié)議。
1. 為類型轉(zhuǎn)換定義類層次
- 可以將類型轉(zhuǎn)換用在類和子類的層次結(jié)構(gòu)上钻哩,檢查特定類實(shí)例的類型并且轉(zhuǎn)換這個(gè)類實(shí)例的類型成為這個(gè)層次結(jié)構(gòu)中的其他類型。
/*
這個(gè)類為任何出現(xiàn)在數(shù)字媒體庫的媒體項(xiàng)提供基礎(chǔ)功能肛冶。
特別的街氢,它聲明了一個(gè) String 類型的 name 屬性,和一個(gè) init(name:) 初始化器睦袖。
*/
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
/*
子類 Movie 封裝了與電影相關(guān)的額外信息
在父類(或者說基類)的基礎(chǔ)上增加了一個(gè) director(導(dǎo)演)屬性珊肃,和相應(yīng)的初始化器。
*/
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
/*
子類 Song馅笙,在父類的基礎(chǔ)上增加了一個(gè) artist(藝術(shù)家)屬性伦乔,和相應(yīng)的初始化器
*/
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
/*
創(chuàng)建了一個(gè)數(shù)組常量 library,包含兩個(gè) Movie 實(shí)例和三個(gè) Song 實(shí)例董习。
library 的類型是在它被初始化時(shí)根據(jù)它數(shù)組中所包含的內(nèi)容推斷來的烈和。
Swift 的類型檢測(cè)器能夠推斷出 Movie 和 Song 有共同的父類 MediaItem,所以它推斷出 [MediaItem] 類作為 library 的類型
*/
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
] // 數(shù)組 library 的類型被推斷為 [MediaItem]
library 里存儲(chǔ)的媒體項(xiàng)依然是 Movie 和 Song 類型的阱飘。
但是斥杜,若你迭代它,依次取出的實(shí)例會(huì)是 MediaItem 類型的沥匈,而不是 Movie 和 Song 類型蔗喂。
為了讓它們作為原本的類型工作,你需要檢查它們的類型或者向下轉(zhuǎn)換它們到其它類型高帖。
2. 檢查類型
- 用類型檢查操作符(is)來檢查一個(gè)實(shí)例是否屬于特定子類型缰儿。
- 若實(shí)例屬于那個(gè)子類型,類型檢查操作符返回 true散址,否則返回 false乖阵。
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs") // 打印“Media library contains 2 movies and 3 songs”
迭代了數(shù)組 library 中的所有項(xiàng)宣赔。
每一次,for-in 循環(huán)設(shè)置 item 常量為數(shù)組中的下一個(gè) MediaItem 實(shí)例瞪浸。
若當(dāng)前 MediaItem 是一個(gè) Movie 類型的實(shí)例儒将,item is Movie 返回 true,否則返回 false对蒲。
同樣的钩蚊,item is Song 檢查 item 是否為 Song 類型的實(shí)例。
在循環(huán)結(jié)束后蹈矮,movieCount 和 songCount 的值就是被找到的屬于各自類型的實(shí)例的數(shù)量砰逻。
3. 向下轉(zhuǎn)型
- 某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類。
- 當(dāng)確定是這種情況時(shí)泛鸟,你可以嘗試用類型轉(zhuǎn)換操作符(as? 或 as!)向下轉(zhuǎn)到它的子類型蝠咆。
- 因?yàn)橄蛳罗D(zhuǎn)型可能會(huì)失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式北滥。
- 條件形式 as? 返回一個(gè)你試圖向下轉(zhuǎn)成的類型的可選值刚操。
- 當(dāng)你不確定向下轉(zhuǎn)型可以成功時(shí),用類型轉(zhuǎn)換的條件形式(as?)再芋。條件形式的類型轉(zhuǎn)換總是返回一個(gè)可選值赡茸,并且若下轉(zhuǎn)是不可能的,可選值將是 nil祝闻。這使你能夠檢查向下轉(zhuǎn)型是否成功。
- 強(qiáng)制形式 as! 把試圖向下轉(zhuǎn)型和強(qiáng)制解包轉(zhuǎn)換結(jié)果結(jié)合為一個(gè)操作遗菠。
- 只有你可以確定向下轉(zhuǎn)型一定會(huì)成功時(shí)联喘,才使用強(qiáng)制形式(as!)。當(dāng)你試圖向下轉(zhuǎn)型為一個(gè)不正確的類型時(shí)辙纬,強(qiáng)制形式的類型轉(zhuǎn)換會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤豁遭。
- 條件形式 as? 返回一個(gè)你試圖向下轉(zhuǎn)成的類型的可選值刚操。
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
嘗試將 item 轉(zhuǎn)為 Movie 類型。若成功贺拣,設(shè)置一個(gè)新的臨時(shí)常量 movie 來存儲(chǔ)返回的可選 Movie 中的值蓖谢。
若向下轉(zhuǎn)型成功,然后 movie 的屬性將用于打印一個(gè) Movie 實(shí)例的描述譬涡,包括它的導(dǎo)演的名字 director闪幽。
相似的原理被用來檢測(cè) Song 實(shí)例,當(dāng) Song 被找到時(shí)則打印它的描述(包含 artist 的名字)涡匀。
注意:轉(zhuǎn)換沒有真的改變實(shí)例或它的值盯腌。根本的實(shí)例保持不變;只是簡單地把它作為它被轉(zhuǎn)換成的類型來使用陨瘩。
4. Any 和 AnyObject 的類型轉(zhuǎn)換
Swift 為不確定類型提供了兩種特殊的類型別名:
- Any 可以表示任何類型腕够,包括函數(shù)類型级乍。
- AnyObject 可以表示任何類類型的實(shí)例。
只有當(dāng)你確實(shí)需要它們的行為和功能時(shí)才使用 Any 和 AnyObject帚湘。最好還是在代碼中指明需要使用的類型玫荣。
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
- 可以在 switch 表達(dá)式的 case 中使用 is 和 as 操作符來找出只知道是 Any 或 AnyObject 類型的常量或變量的具體類型。
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael
- Any 類型可以表示所有類型的值大诸,包括可選類型捅厂。
- Swift 會(huì)在你用 Any 類型來表示一個(gè)可選值的時(shí)候,給你一個(gè)警告底挫。
- 如果你確實(shí)想使用 Any 類型來承載可選值恒傻,你可以使用 as 操作符顯式轉(zhuǎn)換為 Any。
let optionalNumber: Int? = 3
things.append(optionalNumber) // 警告
things.append(optionalNumber as Any) // 沒有警告