★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9739734.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
类型转换是一种检查实例类型或将该实例视为与其自身类层次结构中其他位置不同的超类或子类的方法。
Swift中的类型转换通过is
and as
运算符实现。这两个运算符提供了一种简单而富有表现力的方式来检查值的类型或将值转换为其他类型。
您还可以使用类型强制转换来检查类型是否符合协议,如检查协议一致性中所述。
定义类型转换的类层次结构
您可以将类型转换与类和子类的层次结构一起使用,以检查特定类实例的类型,并将该实例转换为相同层次结构中的另一个类。下面的三个代码段定义了类的层次结构和包含这些类的实例的数组,以供类型转换示例使用。
第一个代码段定义了一个名为的新基类MediaItem
。此类为出现在数字媒体库中的任何项目提供基本功能。具体来说,它声明一个name
type属性String
和一个初始化器。(假定所有媒体项目(包括所有电影和歌曲)都将具有名称。)init name
- class MediaItem {
- var name: String
- init(name: String) {
- self.name = name
- }
- }
下一个代码段定义的两个子类MediaItem
。第一个子类Movie
封装有关电影或电影的其他信息。它director
在基MediaItem
类的顶部添加一个属性,并带有一个相应的初始化程序。第二个子类,在基类的顶部Song
添加一个artist
属性和初始化程序:
- 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)
- }
- }
最后的代码段创建一个名为的常量数组library
,其中包含两个Movie
实例和三个Song
实例。library
通过使用数组文字的内容对其进行初始化来推断数组的类型。Swift的类型检查器能够推导该类型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")
- ]
- // the type of "library" is inferred to be [MediaItem]
存储在其中的项目library
仍然是静止的,Movie
并且是Song
后台的实例。但是,如果您遍历此数组的内容,则返回的项将键入为MediaItem
,而不是Movie
或Song
。为了使用它们作为其本机类型,您需要检查它们的类型,或将它们向下转换为其他类型,如下所述。
检查类型
使用类型检查运算符(is
)检查实例是否属于某个子类类型。类型检查运算符将返回true
实例是否属于该子类类型,false
如果不是。
下面的例子定义两个变量,movieCount
并且songCount
,其计数的数量Movie
和Song
实例中library
阵列:
- 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")
- // Prints "Media library contains 2 movies and 3 songs"
本示例遍历library
数组中的所有项目。每次循环时,for
- in
循环将item
常量设置MediaItem
为数组中的下一个常量。
item is Movie
true
如果当前MediaItem
是Movie
实例,false
则返回,如果不是,则返回。同样,检查项目是否为实例。在- 循环的末尾,和的值包含找到每种类型的实例数的计数。item is Song
Song
for
in
movieCount
songCount
MediaItem
下垂
某个类类型的常量或变量实际上可能是指幕后子类的实例。如果您认为是这种情况,可以尝试使用类型转换运算符(as?
或as!
)将其转换为子类类型。
由于向下转换可能会失败,因此类型转换运算符采用两种不同的形式。条件格式会as?
返回您要向下转换的类型的可选值。强制形式as!
尝试向下转换并强制将结果作为单个复合动作展开。
as?
当您不确定向下转换是否成功时,请使用类型转换运算符()的条件形式。这种形式的运算符将始终返回一个可选值,该值将在nil
无法向下转换的情况下返回。这使您能够检查下调是否成功。
as!
仅当您确定向下转换将始终成功时,才使用类型转换运算符()的强制形式。如果您尝试向下转换为不正确的类类型,则此形式的运算符将触发运行时错误。
下面的示例对中的每个MediaItem
进行迭代library
,并为每个项目打印适当的描述。为此,它需要将每个项目作为true Movie
或访问Song
,而不仅仅是访问a MediaItem
。为了使它能够访问director
或artist
属性Movie
或Song
在说明书中使用,这是必需的。
在此示例中,数组中的每个项目可能是Movie
,也可能是Song
。您事先不知道要为每个项目使用哪个实际类,因此as?
每次通过循环使用类型强制转换运算符()的条件形式检查向下转换是适当的:
- 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
为Movie
。因为item
是MediaItem
实例,所以可能是Movie
; 同样,它也可能是一个Song
,甚至仅仅是一个base MediaItem
。由于存在这种不确定性,当尝试向下转换为子类类型时,类型转换as?
运算符的形式将返回一个可选值。的结果为类型,或“可选”。item as? Movie
Movie?
Movie
Movie
当应用于Song
库数组中的实例时,向下转换失败。为了解决这个问题,上面的示例使用可选绑定来检查该可选对象是否Movie
实际包含一个值(即,找出向下转换是否成功。)此可选绑定被写为“ ”,可以表示为:if let movie = item as? Movie
“尝试访问item
的Movie
。如果成功,则将一个新的临时常数设置为movie
存储在返回的optional中的值Movie
。”
如果向下转换成功,movie
则使用的属性来打印该Movie
实例的描述,包括其名称director
。使用类似的原理检查Song
实例,并在库中找到artist
a时打印适当的描述(包括名称)Song
。
注意
投射实际上不会修改实例或更改其值。基础实例保持不变;简单地将其视为已强制转换为其类型的实例并进行访问。
类型转换为Any和AnyObject
Swift提供两种特殊类型来处理非特定类型:
Any
可以代表任何类型的实例,包括函数类型。AnyObject
可以代表任何类类型的实例。
使用Any
和AnyObject
只有当你明确需要的行为和能力,他们提供的。最好对要在代码中使用的类型进行具体说明。
这是一个Any
用于混合使用不同类型(包括函数类型和非类类型)的示例。该示例创建一个名为的数组things
,该数组可以存储type的值Any
:
- 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)" })
该things
数组包含两个Int
值,两个Double
值,一个String
值,一个类型的元组,电影“ Ghostbusters”,以及一个带一个值并返回另一个值的闭包表达式。(Double, Double)
String
String
地发现,仅已知类型的固定或可变的特定类型Any
或AnyObject
,你可以使用一个is
或as
一个模式switch
语句的情况。下面的示例遍历things
数组中的项目,并使用switch
语句查询每个项目的类型。switch
语句的几种情况将它们的匹配值绑定到指定类型的常量,以使其值能够被打印:
- 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
类型表示任何类型的值,包括可选类型。如果您在Any
期望使用类型值的情况下使用可选值,则Swift会警告您。如果确实需要使用可选值作为Any
值,则可以使用as
运算符将可选值显式转换为Any
,如下所示。
- let optionalNumber: Int? = 3
- things.append(optionalNumber) // Warning
- things.append(optionalNumber as Any) // No warning