「翻译」 Swift 类型转换
这是对 swift 2.2 中 类型转换 一节的翻译
第一次翻译这么长的。。。好麻烦
类型转换
类型转换可以判断实例的类型,也可以将该实例在其所在的类层次中视为其父类或子类的实例。
Swift 中的类型转换使用 is
和 as
作为操作符。这两个操作符使用了一种简单传神的方式来检查一个值的类型或将某个值转换为另一种类型。
你还可以使用类型转换来检查类型是否符合一个协议,参考协议实现的检查。
为类型转换定义类层次
你可以在类及其子类层次中使用类型转换判断特定类实例的类型,或在同一类层次中将该实例类型转换为另一类型。下面的三段代码定义了一个类层次以及一个包含了这些类实例的数组,作为类型转换的例子。
第一个代码片段定义了一个叫做 MediaItem
的新类。这个类为出现在数字媒体库中的所有成员提供了基本的功能。它申明了一个 String
类型的 name
和一个叫做 init
的 name
生成化器(这里假设所有的媒体项目,包括所有电影和音乐,都有一个名字)。
1 | class MediaItem { |
下一个片段定义了两个 MediaItem
的子类。第一个子类 Movie
封装了电影的信息。他在 MediaItem
的基础上添加了名为 director
的属性及其生成化器。第二个子类 Song
增加了名为 artist
的属性及其生成化器。
1 | class Movie: MediaItem { |
最后一个代码段创建了名为 library
的数组常量,其中包含了两个 Movie
和三个 Song
实例。library
数组的类型是在初始化时根据其中内容推断出来的。 Swift 的类型检查器能够推断 Movie
和 Song
有一个共同的父类 MediaItem
, 因此 library
的类型推断为 [MediaItem]
:
1 | let library = [ |
事实上 library
储存的项目依旧是 Movie
和 Song
实例。然而,如果你遍历这个数组的内容,你取出的项目将会是 MediaItem
类型而非 Movie
或 Song
类型。为了使用他们原生的类型,你需要检查他们的类型或将他们向下转换为不同的类型,如下所述。
检查类型
使用类型检查操作符 is
来检查一个实例是否属于一个特定的子类。如果实例属于该子类,类型检查操作符返回 true
,否则返回 false
。
下面的例子定义了两个变量,movieCount
和 songCount
,用来计算数组 library
中 Movie
和 Song
实例的个数:
1 | var movieCount = 0 |
这个例子遍历了整个 library
数组。每一轮中,for-in
的循环都将 item
设置为数组中的当前 MediaItem
。
如果当前 MediaItem
是 Movie
类型的实例,item is Movie
返回 true
,反之返回 false
。同样的,item is Song
检查了该对象是否为 Song
类型的实例。在 for-in
循环的最后,movieCount
和 songCount
的值就是数组中对应类型实例的数量。
向下类型转换
某个类型的常量或变量可能引用自一个子类的实例。当你遇到这种情况时你可以尝试使用类型转换操作符(as?
或 as!
)将它向下转换类型至其子类型。
由于向下类型转换有可能失败,类型转换操作符有两个不同形式。条件形式 as?
返回了一个包含你将要向下转换类型的可选类型。强制形式 as!
则将向下转换和强制解包结合为一个步骤。
如果你不确定你向下转换类型是否成功,请使用条件形式的类型转换操作符 as?
。使用条件形式的类型转换操作符总是返回一个可选类型,如果向下转换失败,可选值为 nil
。可以使用这种方法检查向下类型转换是否成功。
当你确信向下转换类型会成功时,请使用强制形式的类型转换操作符 as!
。当你向下转换至一个错误的类型时,强制形式的类型转换操作符会触发一个运行错误。
下面的例子遍历了 library
中所有的 MediaItem
,并打印出相应的描述信息。要这样做的话,每个项目均需要被当做 Movie
或 Song
类型来访问,而不仅仅是 MediaItem
类型。为了在描述信息中使用 Movie
或 Song
的 director
和 artist
属性,这样做是必要的。
在这个例子中,数组中每一个项目的类型都是 Movie
或 Song
。你不知道遍历时项目的确切类型是什么,所以这时使用条件形式的类型转换符(as?
)来检查遍历中每次向下类型转换:
1 | for item in library { |
例子开头尝试将当前 item
当做 Movie
向下转换类型。由于 item
是一个 MediaItem
的实例,它有可能属于 Movie
类型;同样的,也有可能属于 Song
或者仅仅属于 MediaItem
基类。介于这种不确定性,类型转换符 as?
在向下转换类型时返回了一个可选项。item as? Movie
的结果是 Movie?
类型,也就是『可选 Movie
类型』。
当数组中的 Song
实例使用向下转换至 Movie
类型时会失败。为了处理这种情况,上面的例子使用了可选绑定来检查可选 Movie
类型是否包含了一个值(或者说检查向下类型转换是否成功)。这个可选绑定写作『if let movie = item as? Movie
』,读作『尝试将 item
转换至 Movie
类型。如果成功,设置一个新的临时常量 movie
储存返回的可选 Movie
类型 』。
如果向下转换类型成功,movie
的属性将用于输出 Movie
实例的描述信息,包括director
的名字。同理,无论是否在数组中找到 Song
,均可以检查 Song
实例然后输出合适的描述(包括 artist
的名字)。
注意
类型转换不会改变实例及其值。基本的实例不会改变;只是将它当做其转换的类型来访问。
Any 和 AnyObject 的类型转换
Swift 为不确定的类型提供了两种特殊的类型别名:
AnyObject
可以表示任何类类型的实例。Any
可以表示任何类型,包括函数类型。
注意
当你确切需要使用一个功能时再使用Any
和AnyObject
。在写代码时使用更加明确的类型表达总要好一些。
AnyObject 类型
使用 Cocoa APIs 时,你有时会接收到 [AnyObject]
类型的数组,也就是『由任意类型对象构成的数组』。目前 Objective-C 支持显式类型的数组,但是旧版本的语言没有这个功能。然而你仍可以确信这种提供 API 信息的数组中包含的对象的类型。
在这种情况中,你可以使用强制类型转换符(as!
)将数组中的项目向下转换至比 AnyObject
更明确的类型,而无需将可选类型解包。
下面的例子定义了一个 [AnyObject] 类型的数组,并实例化了三个 Movie
类:
1 | let someObjects: [AnyObject] = [ |
由于我们已知这个数组只包含了 Movie
实例,你可以直接使用强制类型的类型转换符(as!
)将其向下转换至非可选的 Movie
类型:
1 | for object in someObjects { |
在这个循环中,直接将 someObject
向下转换为 [Movie]
类型而非挨个向下转换,可以使代码更短:
1 | for movie in someObjects as! [Movie] { |
Any 类型
这里有一个使用 Any
类型来对不同类型进行操作的例子,包含了函数类型以及非类类型。这个例子定义了一个名为 things
的数组用于储存 Any
类型的值:
1 | var things = [Any]() |
这个 things
数组包含了两个 Int
值、两个 Double
值、一个 String
值、一个 (Double, Double)
的元组、Movie
实例『Ghostbusters』、以及一个接收 String
值并返回 String
值得闭包表达式。
你可以在 switch
结构的 case 中使用 is
和 as
操作符找出已知 Any 或 AnyObject 类型的常量或变量的具体类型。下面的例子使用 switch
语句遍历了 things
数组并查询每一项的类型。其中几个 switch
的 cases 将确定的值和确定类型的常量绑定在一起,使其值可以被输出:
1 | for thing in things { |