ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## POLYMORPHISM AND TYPE CASTING Polymorphism 译为多态,指的是在类的继承中,子类会继承父类的属性、方法,多态即指子类可以拥有父类或自身定义的两种行为,你可以为其选择: ~~~ class Album { var name: String init(name: String) { self.name = name } } class StudioAlbum: Album { var studio: String init(name: String, studio: String) { self.studio = studio super.init(name: name) } } class LiveAlbum: Album { var location: String init(name: String, location: String) { self.location = location super.init(name: name) //子类调用父类方法,实现父类的行为 } } ~~~ 当子类想要实现自己的行为时,可以通过 `override`关键字 重写 父类方法,如此将会实现子类自己定义的方法行为: ~~~ class LiveAlbum: Album { var location: String init(name: String, location: String) { self.location = location super.init(name: name) } override func getPerformance() -> String { return "The live album \(name) sold lots" } } ~~~ 总而言之,一个对象可以同时实现自己类的行为和其父类的行为,这称为「多态」。 ### Converting types with type casting 这种情况时有发生:你有一个明确声明的对象,但你知道它其实是另一种类型(比如上面的继承类StudioAlbum 和 LiveAlbum 被当做 Album 保存在数组中,因为它们继承于 Album 所以是允许的),当需要调用方法时,Swift 可能不知道它的真实类型而无法编译,解决办法是 type casting,即类型转换,可以将一个对象的类型转为另一种类型: ~~~ for album in allAlbums { print(album.getPerformance()) } //根据上面代码块的内容 ~~~ `allAlbums` 数组拥有三个类型为 `Album` 的对象,但是其中两个我们知道是`StudioAlbum` 和 `LiveAlbum`,但是 Swift 却不知道,如果你想执行`print(album.studio)` 则无法编译,因为只有 `StudioAlbum`拥有那个属性。 Type casting 有三种形式,但常见的只有两种:`as?` 和 `as!`,分别是可选向下转型以及强制向下转型,前者会返回一个转型后的可选值(optional value),若转型失败会返回nil;当你确定可以转型成功时使用后者,如果转型失败可能导致应用崩溃: *P.S.「转型」并不是指真的改变实例或它的值,而只是告诉 Swift 把这个对象看做某个类的实例。* ~~~ for album in allAlbums { let studioAlbum = album as? StudioAlbum } ~~~ studioAlbum 变量将会拥有一个StudioAlbum?类型数据或是nil,这经常与`if let`配合使用来自动解包 optional 值: ~~~ for album in allAlbums { print(album.getPerformance()) if let studioAlbum = album as? StudioAlbum { print(studioAlbum.studio) } else if let liveAlbum = album as? LiveAlbum { print(liveAlbum.location) } } ~~~ 遍历 allAlums 数组内的对象,并判断它们是否为特定子类,如果是,调用子类的方法/属性。 强制向下转型(forced downcasting)就相当于转型并强制拆包,返回的是一个非 optional 值,可以直接使用: ~~~ var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio") var allAlbums: [Album] = [taylorSwift, fearless] for album in allAlbums { let studioAlbum = album as! StudioAlbum print(studioAlbum.studio) //便利数组时如果数组内有 liveAlbum 类的实例就会 crash,因为使用了强制转型 //所以为了不 crash,只存放 `StudioAlbum` 实例在数组中 } ~~~ Swfit 也允许将转型写在数组遍历层,在数组便利初始就将数据转型,如此更有效率: ~~~ for album in allAlbums as! [StudioAlbum] { print(album.studio) //相当于省去了 let studioAlbum = album as! StudioAlbum } ~~~ 但这么用必须得确保数组中所有实例都是 StudioAlbum 类型,否则会crash。 可选向下转型(optional downcasting)也可以这么用,但因为这样做有可能提供给「遍历」一个nil,所以需要用 ??(nil coalescing operator) 来确保提供给 loop 一个值: ~~~ for album in allAlbums as? [LiveAlbum] ?? [LiveAlbum]()) { print(album.location) } ~~~ 这样相当于:尝试将对象从 `allAlbums` 转为 `LiveAlbum` 类型,如果失败,就创建一个空的 `LiveAlbum` 对象来替代,这相当于啥也没做。这有可能会用得上,不过最好不这么做。