Swift 型別轉換 (Type Casting)
型別轉換 (Type Casting) 允許你在執行時檢查一個實例的型別,或者將該實例視為其繼承層級中的另一個父類別或子類別。
Swift 的型別轉換使用 is 和 as 運算子實現。
定義類別層級
為了示範,我們先定義一個基礎類別 MediaItem 和兩個子類別 Movie 與 Song:
class MediaItem {
var name: String
init(name: String) { self.name = name }
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
// 建立一個包含 Movie 和 Song 的陣列
// Swift 的型別推斷會將其型別推斷為 [MediaItem]
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")
]
檢查型別 (Checking Type)
使用 is 運算子來檢查一個實例是否屬於某個特定子類別。如果不確定,它會回傳 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")
向下轉型 (Downcasting)
常數或變數在幕後可能實際上屬於一個更具體的子類別。當你相信是這種情況時,你可以嘗試使用 as? 或 as! 運算子將其向下轉型 (Downcast) 至子類別型別。
as?(Conditional Downcast):回傳一個 Optional 值。如果轉型失敗,回傳nil。推薦使用這個方式,因為它最安全。as!(Forced Downcast):強制轉型。如果轉型失敗,程式會當機 (Crash)。只有在你百分之百確定型別正確時才使用。
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)")
}
}
Any 和 AnyObject
Swift 提供了兩個特殊的別名 (Type Aliases) 來處理非特定型別:
AnyObject:可以代表任何 Class 型別的實例。Any:可以代表任何型別的實例,包括函式型別、Struct、Enum,甚至是 Int, Double 等基本型別。
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"))
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: // 也可以在 switch 中使用 as 模式匹配
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)")
default:
print("something else")
}
}
雖然
Any 很靈活,但 Swift 是強型別語言。為了程式碼的安全性與效能,應盡量避免使用 Any,除非你真的需要與非特定型別的 API 互動 (例如某些舊的 Objective-C API)。