★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9739734.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
类型转换是一种检查实例类型的方法,或者将该实例视为与其自己的类层次结构中的其他位置不同的超类或子类。
Swift中的类型转换是使用is
和as
运算符实现的。这两个运算符提供了一种简单而富有表现力的方法来检查值的类型或将值转换为其他类型。
您还可以使用类型转换来检查类型是否符合协议,如检查协议一致性中所述。
为类型转换定义类层次结构
您可以使用类型转换和类和子类的层次结构来检查特定类实例的类型,并将该实例转换为同一层次结构中的另一个类。下面的三个代码片段定义了类的层次结构和包含这些类的实例的数组,用于类型转换的示例。
第一个片段定义了一个名为的新基类MediaItem
。此类为数字媒体库中显示的任何类型的项目提供基本功能。具体来说,它声明了一个name
类型的属性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
,并打印每个项目的相应说明。要做到这一点,它需要将每个项目作为一个真实Movie
或Song
,而不仅仅作为一个MediaItem
。这是为了必要为它是能够访问director
或artist
的属性Movie
或Song
在说明中使用。
在此示例中,数组中的每个项可能是a Movie
,或者可能是a Song
。您事先并不知道每个项目使用哪个实际类,因此使用类型as?
转换operator()的条件形式来检查每次循环时的向下转换是合适的:
- 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
,甚至只是一个基础MediaItem
。由于这种不确定性,as?
类型转换运算符的形式在尝试向下转换为子类类型时返回一个可选值。结果是类型或“可选”。item as? Movie
Movie?
Movie
Movie
应用于Song
库阵列中的实例时,向下转换失败。为了解决这个问题,上面的示例使用可选绑定来检查可选Movie
实际上是否包含一个值(即,找出downcast是否成功。)此可选绑定写为“ ”,可以读作:if let movie = item as? Movie
“尝试访问item
为Movie
。如果成功,则设置一个新的临时常量,调用movie
存储在返回的可选中的值Movie
。“
如果向下转换成功,movie
则使用属性来打印该Movie
实例的描述,包括其名称director
。类似的原则用于检查Song
实例,并在库中找到artist
时打印适当的描述(包括名称)Song
。
注意
转换实际上不会修改实例或更改其值。基础实例保持不变; 它被简单地处理和访问,作为它的类型的实例。
为Any和AnyObject键入Casting
Swift提供了两种特殊类型来处理非特定类型:
Any
可以表示任何类型的实例,包括函数类型。AnyObject
可以表示任何类类型的实例。
使用Any
和AnyObject
只有当你明确需要的行为以及它们提供的功能。最好具体了解您希望在代码中使用的类型。
以下是使用Any
不同类型混合的示例,包括函数类型和非类类型。该示例创建一个名为的数组things
,该数组可以存储类型的值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
类型表示任何类型的值,包括可选类型。如果您使用期望值为type的可选值,Swift会向您发出警告Any
。如果确实需要使用可选值作为Any
值,则可以使用as
运算符显式地将可选值转换为Any
,如下所示。
- let optionalNumber: Int? = 3
- things.append(optionalNumber) // Warning
- things.append(optionalNumber as Any) // No warning