子类可以为继承来的实例方法(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 的,这样的类是不可被继承的,否则会报编译错误。

Swift-重写(Override)的更多相关文章

  1. C#中隐藏(new)、方法重写(override)、重载(overload)的区别

    转自:http://www.cnblogs.com/glife/archive/2009/12/28/1633947.html 重载.重写和隐藏的定义: 重载:public string ToStri ...

  2. 重写(Override)与重载(Overload)

    重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类 ...

  3. Java 重写(Override)与重载(Overload)

    1.重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变.即外壳不变,核心重写! 参数列表和返回值类型必须与被重写方法相同. 访问权限必须低于父类中 ...

  4. 重载(overload),覆盖/重写(override),隐藏(hide)

    写正题之前,先给出几个关键字的中英文对照,重载(overload),覆盖/重写(override),隐藏(hide).在早期的C++书籍中,常常把重载(overload)和覆盖(override)搞错 ...

  5. Java 读书笔记 (十七) Java 重写(Override)与重载(Overload)

    重写(Override) 重写是子类对父类的允许访问的方法的实现过程重新编写,返回值和形参都不能改变,即外壳不变,核心重写. // 如果重写不是相当于重新定义了一个方法?那为什么不直接写,还要exte ...

  6. java学习笔记(十一):重写(Override)与重载(Overload)

    重写(Override) 重写是子类对父类的允许访问的方法的进行重新编写, 但是返回值和形参都不能改变. 实例 class Animal{ public void run(){ System.out. ...

  7. 重写Override ToString()方法

    使用一个小例子来演示: 创建一个普通类别: class Ax { private int _ID; public int ID { get { return _ID; } set { _ID = va ...

  8. Java - 21 Java 重写(Override)与重载(Overload)

    Java 重写(Override)与重载(Overload) 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变.即外壳不变,核心重写! 重写的好 ...

  9. 重写(Override) 重载(Overload)

    重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重载(Overload)- 参数必须不同 重载(overloadin ...

  10. Java面向对象---重写(Override)与重载(Overload)

    一.重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说 ...

随机推荐

  1. html5 video获取当前时间和视频总时间长度

    html: <video id="video-active" class="video-active" width="640" hei ...

  2. Jupyter Notebook的魔法

    在腾讯云上搭建了一个Jupyter Notebook 还是很好用的, 这里总结了一些看到的小技巧及快捷键使得我们更加快捷的完成工作. Notebook 与 Vim 有些类似,有两种键盘输入模式,即命令 ...

  3. 最新版的stm32f1xx.h文件中取消了u8, u16, u32的类型定义

    使用芯片stm32f103zet6和stm32l151c8t6,在移植程序时发现,编译器提示u8未定义: 在Keil MDK 开发环境里,st定义无符号32位整形数据有很多种表示方法: 1 unsig ...

  4. Python学习手册之字符类和元字符深入

    在上一篇文章中,我们介绍了 Python 的正则表达式和元字符,现在我们介绍 Python 的字符类和对元字符进行深入讲解.查看上一篇文章请点击:https://www.cnblogs.com/dus ...

  5. 函数调用方法之__cdecl与_stdcall

    在debug VS c工程文件时,碰到cannot convert from 'int (__cdecl *)(char *)' to 'xxx'这个问题,发现问题在于typedef函数指针类型时,将 ...

  6. QT Creator 转VS2017+QT工程

    因为刚学QT 不熟悉其QT Creator 的开发环境,所以打算用VS2017来开发,因为要跨平台所以打算写出来的代码可以放在linux上编译. 这时就需要将VS2017的工程转换成QT Creato ...

  7. VMWare虚拟机的网络类型配置选择详解

    VMWare虚拟机网络有三种类型,当然还有最后一种类型就是“不使用网络连接”,哈哈....... VMWare在安装会有让选择网络类型的选项,如果不确认使用那一种网络类型,也可以先随便选择一种网络类型 ...

  8. BZOJ1432_Function_KEY

    题目传送门 找规律. 画一个像这样的图: 不同颜色为不同层,因为函数图像可对称,所以只考虑K<=N/2的情况. 最小为min(K,N-K+1)*2. N=1时特殊考虑,答案为1. code: # ...

  9. 北京Uber优步司机奖励政策(1月5日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  10. 北京Uber优步司机奖励政策(12月11日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...