Swift 5.x - 类型转换(中文文档)

1,942 阅读9分钟

引言

继续学习Swift文档,从上一章节:错误处理,我们学习了Swift错误处理相关的内容,主要有使用throwing函数,throw抛出错误、使用do-catch来处理错误、将错误转为可选值(使用try?)、禁用错误传递(使用try!)、延迟操作处理(使用defer关键词)等这些内容。现在,我们学习Swift类型转换的相关内容。由于篇幅较长,这里分篇来记录,接下来,开始吧!

下一章节:嵌套类型

类型转换

类型转换是一种检查实例类型的方法,或者将该实例视为不同于其自身类层次结构中其他地方的父类或子类。

Swift中的类型转换是通过is和as运算符实现的。这两个运算符提供了一种简单而富有表现力的方法来检查值的类型或将值转换为不同的类型。

您还可以使用类型转换来检查类型是否符合协议,如检查协议一致性中所述。

1 为类型转换定义类层次结构

可以将类型转换与类和子类的层次结构一起使用,以检查特定类实例的类型,并将该实例强制转换为同一层次结构中的另一个类。下面的三个代码片段定义了类的层次结构和包含这些类实例的数组,用于类型转换的示例中。

第一个代码段定义了一个新的基类MediaItem。此类为出现在数字媒体库中的任何类型的项目提供基本功能。具体地说,它声明了String类型的name属性和init名称初始值设定项。(假设所有媒体项目,包括所有电影和歌曲,都有一个名称。)

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

下一个片段定义了MediaItem的两个子类。第一个子类Movie封装了有关电影或电影的附加信息。它使用相应的初始值设定项,在基MediaItem类的顶部添加一个director属性。第二个子类Song在基类的顶部添加了一个艺术家属性和初始值设定项:

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父类,因此它推断出library数组的[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")
]
// the type of "library" is inferred to be [MediaItem]

library中存储的项目仍然是幕后的Movie和Song实例。但是,如果迭代此数组的内容,则返回的项目将被键入MediaItem,而不是Movie或Song。为了将它们作为自己本身的类型使用,需要检查它们的类型,或者将它们下放到其他类型,如下所述。

2 检查类型

使用类型检查运算符(is)检查实例是否属于某个子类类型。如果实例属于该子类类型,则类型检查运算符返回true;如果不是,则返回false。

下面的示例定义了两个变量movieCount和songCount,用于计算library数组中Movie和Song实例的数量:

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如果当前MediaItem是电影实例,则返回true;如果不是,则返回false。类似地,item is Song检查item是否是Song实例。在for-in循环的末尾,movieCount和songCount的值包含找到每种类型的MediaItem实例的计数。

3 Downcasting(向下转型)

某个类类型的常量或变量实际上可能在幕后引用子类的实例。在这种情况下,可以尝试使用类型转换运算符(as?或者as!)。

由于向下转型可能失败,类型转换运算符有两种不同的形式。条件形式,比如?,返回要向下转换到的类型的可选值。强迫的形式,比如!,尝试下推并强制将结果展开为单个复合操作。

使用类型转换运算符(as?)的条件形式当你不确定向下转型是否会成功。这种形式的运算符将始终返回一个可选值,如果无法进行向下转型,则该值将为零。这使您能够检查是否成功执行了向下转型。

使用类型转换运算符的强制形式(as!)只有当你确信向下转型总会成功的时候。如果您尝试向下转型为不正确的类类型,这种形式的运算符将触发运行时错误。

下面的示例迭代库中的每个MediaItem,并为每个项打印适当的描述。要做到这一点,它需要以真正的Movie或Song的形式访问每个项目,而不仅仅是MediaItem。这是必需的,以便它能够访问Movie或Song的director或artist属性,以便在描述中使用。

在本例中,数组中的每个项目可能是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是MediaItem实例,所以它可能是Movie;同样,它也可能是Song,甚至可能只是一个基本的mediaItem。因为这种不确定性,as?类型转换运算符的形式在尝试向下转型为子类类型时返回可选值。项目结果为as?的Movie是Movie?,或“optional Movie”。

当应用于library数组中的Song实例时,向下转型到Movie是失败的。为了解决这个问题,上面的例子使用可选绑定来检查可选的Movie是否真的包含一个值(也就是说,确定向下转型是否成功)“if let movie = item as? Movie”,可以理解为:

“尝试将item作为Movie访问。如果成功,请将名为movie的新临时常量设置为存储在返回的可选Movie中的值。“ 如果向下转型成功,则movie的属性将用于打印该Moview实例的描述,包括其director的name。类似的原理用于检查Song实例,并在library中找到Song时打印适当的描述(包括artist的name)。

注意
强制转换实际上不会修改实例或更改其值。底层实例保持不变;它只是作为它被强制转换到的类型的实例来处理和访问。

4 Any和AnyObject的类型转换

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值、一个字符串值、一个tuple类型(Double,Double)、movie“Ghostbusters”和一个接受字符串值并返回另一个字符串值的闭包表达式。 要发现只知道Any或AnyObject类型的常量或变量的特定类型,可以在switch语句的情况下使用is或as模式。下面的示例迭代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

总结

该章节主要讲了类型转换的语法实现,主要涉及到:

  • as!和as?两个类型进行Downcasting(向下转型)的语法
  • 可以用is来进行类型检查的关键词
  • Any和AnyObject的类型转换

好了,这节内容比较简单,已经了解的朋友可以重新巩固一下,有收获的,麻烦点个赞哦~谢谢啦!

上一章节:错误处理

下一章节:嵌套类型

参考文档:Swift - Type Casting