Swift-类型转换、校验

类型转换是一种检查实例类型的方法,或者将该实例视为来自其自身类层次结构中的其他地方的不同超类或子类。
Swift中的类型转换使用is和as运算符实现。 这两个操作符提供了一种简单和表达方式来检查值的类型或将值转换为不同类型。
您还可以使用类型转换来检查类型是否符合协议,如检查协议一致性中所述。

定义类型转换的类层次结构

  • 您可以使用具有类和子类的层次结构的类型转换来检查特定类实例的类型,并将该实例转换到同一层次结构中的另一个类。 下面的三个代码片段定义了类的层次结构和包含这些类的实例的数组,用于类型转换的示例。
  • 第一个片段定义了一个名为MediaItem的新基类。 此类为数组library中出现的任何类型的项提供基本类型。 具体来说,它声明了一个String类型的name属性,以及一个初始化名称初始化程序。 (假设所有元素,包括所有movie和song,都将有一个名称。)
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
  • 下一个片段定义了MediaItem的两个子类。 第一个子类Movie,封装了关于电影或电影的附加信息。 它在基本MediaItem类的顶部添加了一个director属性,以及一个相应的初始化器。 第二个子类,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]的库数组:
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")
]
  • 存储在library中的元素仍然是Movie和Song的实例。 但是,如果迭代此数组的内容,则您接收到的项目将键入为MediaItem,而不是Movie或Song。 为了使用它们是它们自身的类型,你需要检查它们的类型,或者将它们降级到不同的类型,如下所述。

类型校检

  • 使用类型检查运算符(is)来检查实例是否是某个子类型。 如果实例是该子类型,则类型检查运算符返回true,否则返回false。
  • 下面的示例定义了两个变量movieCount和songCount,它们计算库数组中Movie和Song实例的数量:
var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {//Item是Movie的子类
        movieCount += 1
    } else if item is Song {//item是Song的子类
        songCount += 1
    }
}

print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"

下转

  • 某个类类型的常量或变量实际上可能指的是子类的实例。 在这种情况下,你可以尝试使用类型转换运算符(as?或as!)向下转换到子类类型。
  • 因为向下转换可能失败,所以类型转换操作符有两种不同的形式。 条件形式as ?,返回您尝试向下转换的类型的可选值。 强制形式,as!,尝试向下转换并将结果解包为单个复合操作。
  • 当您不确定向下转换是否将成功时,使用类型转换运算符的条件形式(as?)。 此形式的运算符将始终返回一个可选值,如果不可能进行向下转换,则该值将为nil。 这使您能检查一个类型的下转是否成功。
  • 只有当你确信类型下转绝对成功时,才使用强制转换类型转换操作符(as!)。 如果您尝试向下转换为不正确的类类型,则此形式的运算符将触发运行时错误。
  • 下面的示例代码中遍历library数组中的每个MediaItem,并为每个项目打印一个适当的描述。 为此,它需要访问每个item的真实类型是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类型。 由于这种不确定性, 形式的类型转换操作符在尝试向下转换为子类类型时返回可选值。 item的转换结果可能是“Movie”也可能是“Optional Movie”。
  • 当library数组中的类型是Song的时候,下转到Movie类型失败。 为了解决这个问题,上面的例子使用可选的绑定来检查可选的Movie是否实际上包含一个值(也就是说,判断下转是否成功。)这个可选的绑定是写为“if let movie = item as? Movie“,可以读作:“尝试以Movie访问item。 如果这是成功的,请将一个名为movie的新临时常量设置为存储在返回的可选Movie中的值。
  • 如果向下转换成功,则movie的属性用于打印该Movie实例的描述,包括其director的名称。 类似的原理用于检查Song实例,并在library中找到Song打印适当的描述(包括artist姓名)。

***注意****:转换不会实际修改实例或更改其值。 底层实例保持不变; 它被简单地创建并作为它已经被转换到的类型的实例被访问。

对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值,一个String值,一个类型为(Double,Double)的元组,电影“Ghostbusters”和一个闭包表达式,它接受一个String值并返回另一个String值。
  • 只要是Any或AnyObject类型的常量或变量的特定类型,可以在switch语句的case中使用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类型的值。 如果真的需要使用可选值作为Any值,可以使用as运算符将可选值显式转换为Any,如下所示。

let optionalNumber: Int? = 3
things.append(optionalNumber) // Warning
things.append(optionalNumber as Any) // No warning
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容