Swift基础 类型铸造

翻译自:https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

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

Swift中的类型转换是用is``as运算符实现的。这两个运算符提供了一种简单而富有表现力的方式来检查值的类型或将值转换为其他类型。

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

定义类型铸造的类层次结构

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

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

  1. class MediaItem {
  2. ​ var name: String
  3. ​ init(name: String) {
  4. ​ self.name = name
  5. ​ }
  6. }

下一个片段定义了MediaItem的两个子类。第一个子类“Movie会封装有关某部或某部电影的其他信息。它在基础MediaItem类之上添加了一个director属性,并带有相应的初始化器。第二个子类Song在基类之上添加了artist属性和初始化器:

  1. class Movie: MediaItem {
  2. ​ var director: String
  3. ​ init(name: String, director: String) {
  4. ​ self.director = director
  5. ​ super.init(name: name)
  6. ​ }
  7. }
  8. class Song: MediaItem {
  9. ​ var artist: String
  10. ​ init(name: String, artist: String) {
  11. ​ self.artist = artist
  12. ​ super.init(name: name)
  13. ​ }
  14. }

最后一个片段创建一个名为library的常量数组,其中包含两个Movie实例和三个Song实例。通过使用数组文字的内容初始化库数组来推断library数组的类型。Swift的类型检查器能够推断MovieSong具有常见的MediaItem超类,因此它推断出库数组的[MediaItem]类型:

  1. let library = [
  2. ​ Movie(name: “Casablanca”, director: “Michael Curtiz”),
  3. ​ Song(name: “Blue Suede Shoes”, artist: “Elvis Presley”),
  4. ​ Movie(name: “Citizen Kane”, director: “Orson Welles”),
  5. ​ Song(name: “The One And Only”, artist: “Chesney Hawkes”),
  6. ​ Song(name: “Never Gonna Give You Up”, artist: “Rick Astley”)
  7. ]
  8. // the type of “library” is inferred to be [MediaItem]

存储在library中的项目仍然是幕后MovieSong实例。但是,如果您迭代此数组的内容,则您收到的项目被键入为MediaItem,而不是MovieSong。为了将他们作为他们的原生类型,您需要检查他们的类型,或将他们为其他类型,如下所述。

检查类型

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

以下示例定义了两个变量,movieCountsongCount,它们计算library数组中的MovieSong实例数量:

  1. var movieCount = 0
  2. var songCount = 0
  3. for item in library {
  4. ​ if item is Movie {
  5. ​ movieCount += 1
  6. ​ } else if item is Song {
  7. ​ songCount += 1
  8. ​ }
  9. }
  10. print(“Media library contains (movieCount) movies and (songCount) songs”)
  11. // Prints “Media library contains 2 movies and 3 songs”

此示例迭代library数组中的所有项目。在每次传递中,for-in循环将item常量设置为数组中的下一个MediaItem

item is Movie如果当前MediaItemMovie实例,则返回true,如果不是,则返回false。同样,itemisSong,检查项目是否是Song实例。在for-in循环的末尾,movieCountsongCount的值包含每种类型的MediaItem实例数量。

压倒

特定类类型的常量或变量实际上可能指幕后子类的实例。如果您认为情况就是这样,您可以尝试使用类型转换运算符**降到子类类型(as?或者as!)。

由于下调可能会失败,类型转换运算符有两种不同的形式。条件形式as?返回您试图向下转换的类型的可选值。强迫形式,as!,尝试压倒和强迫将结果包装为单个复合动作。

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

使用类型转换运算符的强制形式(as!)只有当你确信沮丧的人会永远成功的时候。如果您尝试将这种形式的运算符降到错误的类类型,将触发运行时错误。

以下示例对library中的每个MediaItem迭代,并为每个项目打印适当的描述。要做到这一点,它需要将每个项目作为真正的MovieSong访问,而不仅仅是作为MediaItem。这是必要的,这样它才能访问MovieSongdirectorartist属性,以便在描述中使用。

在本例中,数组中的每个项目可能是Movie,也可能是Song。您事先不知道每个项目应使用哪个实际类,因此使用类型转换运算符的条件形式是合适的(as?)每次通过循环检查降压:

  1. for item in library {
  2. ​ if let movie = item as? Movie {
  3. ​ print(“Movie: (movie.name), dir. (movie.director)”)
  4. ​ } else if let song = item as? Song {
  5. ​ print(“Song: (song.name), by (song.artist)”)
  6. ​ }
  7. }
  8. // Movie: Casablanca, dir. Michael Curtiz
  9. // Song: Blue Suede Shoes, by Elvis Presley
  10. // Movie: Citizen Kane, dir. Orson Welles
  11. // Song: The One And Only, by Chesney Hawkes
  12. // Song: Never Gonna Give You Up, by Rick Astley

该示例首先尝试将当前item降为Movie。因为itemMediaItem实例,所以它可能是一部Movie;同样,它也可能是一首Song,甚至只是一个基本的MediaItem。因为这种不确定性,as?当尝试将类型转换为子类类型时,类型转换运算符的形式返回一个可选值。item的结果as?Movie类型为Movie?,或“可选Movie”。

当应用于库数组中Song实例时,将向下转换到Movie失败。为了应对这种情况,上面的示例使用可选绑定来检查可选Movie是否真的包含一个值(即找出被关闭的版本是否成功)。这个可选绑定写为“ifletmovie=itemas?Movie”,可以读作:

“尝试将item作为Movie访问。如果成功,请将名为movie的新临时常量设置为存储在返回的可选Movie中的值。”

如果压制成功,则使用movie的属性来打印该Movie实例的描述,包括其director的姓名。类似的原则用于检查Song实例,并在库中找到Song时打印适当的描述(包括artist姓名)。

注意

铸造实际上不会修改实例或更改其值。基础实例保持不变;它只是作为其被转换到的类型的实例进行处理和访问。

任何和AnyObject的类型铸造

Swift 提供了两种特殊类型,用于处理非特定类型:

  • Any可以表示任何类型的实例,包括函数类型。
  • AnyObject可以表示任何类类型的实例。

仅当您明确需要它们提供的行为和功能时,才使用AnyAnyObject。最好具体说明您希望在代码中使用的类型。

以下是使用Any处理不同类型组合的示例,包括函数类型和非类类型。该示例创建一个名为things的数组,可以存储类型为Any的值:

  1. var things: [Any] = []
  2. things.append(0)
  3. things.append(0.0)
  4. things.append(42)
  5. things.append(3.14159)
  6. things.append(“hello”)
  7. things.append((3.0, 5.0))
  8. things.append(Movie(name: “Ghostbusters”, director: “Ivan Reitman”))
  9. things.append({ (name: String) -> String in “Hello, (name)” })

things数组包含两个Int值、两个Double值、一个String值、一个类型的元组(Double,Double)电影“Ghostbusters”和一个接受String值并返回另一个String值的闭包表达式。

To discover the specific type of a constant or variable that’s known only to be of type Any or AnyObject, you can use an is or as pattern in a switch statement’s cases. The example below iterates over the items in the things array and queries the type of each item with a switch statement. Several of the switch statement’s cases bind their matched value to a constant of the specified type to enable its value to be printed:

  1. for thing in things {
  2. ​ switch thing {
  3. ​ case 0 as Int:
  4. ​ print(“zero as an Int”)
  5. ​ case 0 as Double:
  6. ​ print(“zero as a Double”)
  7. ​ case let someInt as Int:
  8. ​ print(“an integer value of (someInt)”)
  9. ​ case let someDouble as Double where someDouble > 0:
  10. ​ print(“a positive double value of (someDouble)”)
  11. ​ case is Double:
  12. ​ print(“some other double value that I don’t want to print”)
  13. ​ case let someString as String:
  14. ​ print(“a string value of "(someString)"“)
  15. ​ case let (x, y) as (Double, Double):
  16. ​ print(“an (x, y) point at (x), (y)”)
  17. ​ case let movie as Movie:
  18. ​ print(“a movie called (movie.name), dir. (movie.director)”)
  19. ​ case let stringConverter as (String) -> String:
  20. ​ print(stringConverter(“Michael”))
  21. ​ default:
  22. ​ print(“something else”)
  23. ​ }
  24. }
  25. // zero as an Int
  26. // zero as a Double
  27. // an integer value of 42
  28. // a positive double value of 3.14159
  29. // a string value of “hello”
  30. // an (x, y) point at 3.0, 5.0
  31. // a movie called Ghostbusters, dir. Ivan Reitman
  32. // Hello, Michael

注意

Any类型表示任何类型的值,包括可选类型。如果您使用可选值,其中需要Any类型的值,Swift会向您发出警告。如果您确实需要将可选值用作Any值,您可以使用as运算符将可选值显式转换为Any,如下所示。

  1. let optionalNumber: Int? = 3
  2. things.append(optionalNumber) // Warning
  3. things.append(optionalNumber as Any) // No warning