Swift-重写(Override)

子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或附属脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。

如果要重写某个特性,你需要在重写定义的前面加上override关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少override关键字的重写都会在编译时被诊断为错误。

override关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。

访问超类的方法,属性及附属脚本

当你在子类中重写超类的方法,属性或附属脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。

在合适的地方,你可以通过使用super前缀来访问超类版本的方法,属性或附属脚本:

  • 在方法someMethod的重写实现中,可以通过super.someMethod()来调用超类版本的someMethod方法。
  • 在属性someProperty的 getter 或 setter 的重写实现中,可以通过super.someProperty来访问超类版本的someProperty属性。
  • 在附属脚本的重写实现中,可以通过super[someIndex]来访问超类版本中的相同附属脚本。

重写方法

在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。

下面的例子定义了Vehicle的一个新的子类,叫Car,它重写了从Vehicle类继承来的description方法:

  1. class Car: Vehicle {
  2. var speed: Double = 0.0
  3. init() {
  4. super.init()
  5. maxPassengers = 5
  6. numberOfWheels = 4
  7. }
  8. override func description() -> String {
  9. return super.description() + "; "
  10. + "traveling at \(speed) mph"
  11. }
  12. }

Car声明了一个新的存储型属性speed,它是Double类型的,默认值是0.0,表示“时速是0英里”。Car有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。

Car重写了继承来的description方法,它的声明与Vehicle中的description方法一致,声明前面加上了override关键字。

Car中的description方法并非完全自定义,而是通过super.description使用了超类Vehicle中的description方法,然后再追加一些额外的信息,比如汽车的当前速度。

如果你创建一个Car的新实例,并打印description方法的输出,你就会发现描述信息已经发生了改变:

  1. let car = Car()
  2. println("Car: \(car.description())")
  3. // Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph

重写属性

你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变。

重写属性的Getters和Setters

你可以提供定制的 getter(或 setter)来重写任意继承来的属性,无论继承来的属性是存储型的还是计算型的属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必需将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。

你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。

注意:如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回super.someProperty来返回继承来的值。正如下面的SpeedLimitedCar的例子所示。

以下的例子定义了一个新类,叫SpeedLimitedCar,它是Car的子类。类SpeedLimitedCar表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的speed属性来实现这个速度限制:

  1. class SpeedLimitedCar: Car {
  2. override var speed: Double {
  3. get {
  4. return super.speed
  5. }
  6. set {
  7. super.speed = min(newValue, 40.0)
  8. }
  9. }
  10. }

当你设置一个SpeedLimitedCar实例的speed属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的speed设置为newValue和40.0中较小的那个。这两个值哪个较小由min函数决定,它是Swift标准库中的一个全局函数。min函数接收两个或更多的数,返回其中最小的那个。

如果你尝试将SpeedLimitedCar实例的speed属性设置为一个大于40mph的数,然后打印description函数的输出,你会发现速度被限制在40mph:

  1. let limitedCar = SpeedLimitedCar()
  2. limitedCar.speed = 60.0
  3. println("SpeedLimitedCar: \(limitedCar.description())")
  4. // SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph

重写属性观察器(Property Observer)

你可以在属性重写中为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看属性观察器

注意:你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供willSet或didSet实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。

下面的例子定义了一个新类叫AutomaticCar,它是Car的子类。AutomaticCar表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。AutomaticCar也提供了定制的description方法,可以输出当前挡位。

  1. class AutomaticCar: Car {
  2. var gear = 1
  3. override var speed: Double {
  4. didSet {
  5. gear = Int(speed / 10.0) + 1
  6. }
  7. }
  8. override func description() -> String {
  9. return super.description() + " in gear \(gear)"
  10. }
  11. }

当你设置AutomaticCar的speed属性,属性的didSet观察器就会自动地设置gear属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位gear的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:

  1. let automatic = AutomaticCar()
  2. automatic.speed = 35.0
  3. println("AutomaticCar: \(automatic.description())")
  4. // AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4

防止重写

你可以通过把方法,属性或附属脚本标记为final来防止它们被重写,只需要在声明关键字前加上@final特性即可。(例如:@final var, @final func, @final class func, 以及 @final subscript)

如果你重写了final方法,属性或附属脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或附属脚本也可以在扩展的定义里标记为 final。

你可以通过在关键字class前添加@final特性(@final class)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。

posted @ 2016-08-26 16:23  J-Vijay  阅读(2144)  评论(0编辑  收藏  举报