类型转换
类型转换
可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
类型转换在Swift中使用is
和as
操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
你也可以用它来检查一个类型是否实现了某个协议。
定义一个类层次作为例子
可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。
/** * 这个类为任何出现在数字媒体库的媒体项提供基础功能 */
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)
}
}
let library = [ // 类型被推断为[MediaItem]
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")
]
print(library) // [Movie,Song,Movie,Song]
推断出[MediaItem]
类作为library
的类型。在幕后library
里存储的媒体项依然是Movie
和Song类型的。但是,若你迭代它,依次取出的实例会是
MediaItem类型的,而不是Movie
和Song
类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型。
检查类型
用类型检查操作符(is
)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true
,否则返回false
。
/** * 这个类为任何出现在数字媒体库的媒体项提供基础功能 */
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String,Song]
var movieCount = 0
var songCount = 0
for i in library {
if i is Movie {
++movieCount
} else if i is Song {
++songCount
}
}
print("\(movieCount) movies,\(songCount) songs") // 2 movies,3 songs
向下转型
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as?
或as!
)。
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式
(conditional form)as?
返回一个你试图向下转成的类型的可选值(optional value)。强制形式``as!
把试图向下转型和强制解包(force-unwraps)转换结果结合为一个操作。
当你不确定向下转型可以成功时,用类型转换的条件形式(as?
)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是nil
。这使你能够检查向下转型是否成功。
只有你可以确定向下转型一定会成功时,才使用强制形式(as!
)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。
/** * 这个类为任何出现在数字媒体库的媒体项提供基础功能 */
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String,Song]
for i in library {
if let item = i as? Movie {
print("Movie: name = \(item.name),director = \(item.director)")
} else if let item = i as? Song {
print("Song: name = \(item.name),artist = \(item.artist)")
}
}
/* 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 */
Any和AnyObject的类型转换
Swift为不确定类型提供了两种特殊的类型别名:
注意,只有当你确实需要它们的行为和功能时才使用Any
和AnyObject
。在你的代码里使用你期望的明确类型总是更好的。
AnyObject类型
当在工作中使用Cocoa APIs时,我们经常会接收到一个[AnyObject]
类型的数组,或者说“一个任意类型对象的数组”。这是因为Objective-C
没有明确的类型化数组。但是,你常常可以从API提供的信息来确定数组中对象的类型。
译者注
这段文档似乎没有及时更新,从Xcode 7和Swift 2.0开始,由于Objective-C引入了轻量泛型,集合类型已经可以类型化了,在Swift中使用Cocoa API也越来越少遇到AnyObject
类型了。详情请参阅Lightweight Generics
和Collection Classes
。
在这些情况下,你可以使用强制形式的类型转换(as
)来下转数组中的每一项到比AnyObject
更明确的类型,不需要可选解包(optional unwrapping)。
/** * 这个类为任何出现在数字媒体库的媒体项提供基础功能 */
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String,artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library: [AnyObject] = [
Movie(name: "2001: A Space Odyssey",director: "Stanley Kubrick"),Movie(name: "Moon",director: "Duncan Jones"),Movie(name: "Alien",director: "Ridley Scott")
]
print(library) // [Movie,Movie]
// 因为知道这个数组只包含Movie 实例,你可以直接用(as!)下转并解包到非可选的Movie类型
for item in library {
let movie = item as! Movie
print("Movie: name = \(movie.name),director = \(movie.director)")
}
print("==========")
// 为了变为一个更简短的形式,下转数组为[Movie]类型而不是下转数组中的每一项
for movie in library as! [Movie] {
print("Movie: name = \(movie.name),director = \(movie.director)")
}
/* Movie: name = 2001: A Space Odyssey,director = Stanley Kubrick Movie: name = Moon,director = Duncan Jones Movie: name = Alien,director = Ridley Scott ========== Movie: name = 2001: A Space Odyssey,director = Ridley Scott */
在上面的代码中,可以下转数组中的各项,也可以直接下转整个数组,前提是实际类型是相同的。
Any类型
使用Any
类型来和混合的不同类型一起工作,包括函数类型和非类类型。
/** * 这个类为任何出现在数字媒体库的媒体项提供基础功能 */
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)
}
}
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)" })
for thing in things {
switch thing {
case 0 as Int: // switch语句的case绑定它们匹配到的值到一个指定类型的常量
print("0 as Int,thing = \(thing)")
case 0 as Double: // switch语句的case绑定它们匹配到的值到一个指定类型的常量
print("0 as Double,thing = \(thing)")
case let i as Int:
print("Int: \(i)")
case let d as Double where d > 0:
print("positive double = \(d),thing = \(thing)")
case is Double:
print("negative double,thing = \(thing)")
case let s as String:
print("string = \(s)")
case let (x,y) as (Double,Double):
print("double tuple = (\(x),\(y))")
case let movie as Movie:
print("movie.name = \(movie.name),director = \(movie.director)")
case let c as String -> String:
print("a closure: " + c("Tim"))
default:
print("other...,thing = \(thing)")
}
}
/* 0 as Int,thing = 0 0 as Double,thing = 0.0 Int: 42 positive double = 3.14159,thing = 3.14159 string = hello double tuple = (3.0,5.0) movie.name = Ghostbusters,director = Ivan Reitman a closure: Hello,Tim */