子类可以为继承来的实例方法(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. PHP运行原理之Opcodes

    在我之前的博客<Laravel5框架性能优化技巧>中提到开启OPcache可以提升php性能.那么为什么开启OPcache就可以提升php运行性能呢?这里就要提到php的运行原理了--Op ...

  2. 在多字节的目标代码页中,没有此 Unicode 字符可以映射到的字符。 (#1113)

    报错 在使用MySQL-Front导入sql文件时报错1113:在多字节的目标代码页中,没有此 Unicode 字符可以映射到的字符. (#1113) 解决方案 导入.sql文件时,单击 选择文件对话 ...

  3. 几个简单if程序的细节比较与加法程序设计

    关于简单的if判断语句的不同写法: 输出0-9十个整数: 第一个程序: #include<stdio.h> #include<stdlib.h> int main() { ; ...

  4. xargs命令的使用过程中一个小领悟:管道与xargs的差别

    对xargs的使用总是比较模糊,大概的理解为:通道中,上一个命令的标准输出逐行作为下一个命令的参数 例如 find /var/temp* | xargs rm -r 功效:找出/var/中所有temp ...

  5. JavaSE思维导图

    Java基础知识:  面向对象:  集合:  多线程.网络编程.反射.设计模式:  常用API:  转载 https://blog.csdn.net/qq_34983808/article/detai ...

  6. Java设计模式(12)——结构型模式之门面模式(Facade)

    一.概述 概念 简要示意图(没有一个统一的UML图) 角色 门面角色:门面模式核心,它被客户端调用,并且熟悉子系统   子系统角色:子系统,子系统并不知道门面的存在,门面对它来说只不过是另外一个客户端 ...

  7. MapWindow记录

    增加MapWinGIS的新功能,编译完MapWinGIS,可以生成Debug和Release版本的x64和Win32四种版本, 自己基于c#的Mapwindow如果要用到新添加的功能,此时就得重新注册 ...

  8. 成都Uber优步司机奖励政策(1月21日)

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

  9. 天津Uber优步司机奖励政策(12月21日到12月27日)

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

  10. CSS3 子节点选择器

    CSS3中新增了几个子元素选择器,大大提高了开发者的开发效率.之前有些要通过为一个个子元素添加class,或者js实现才能实现的效果.现在可以很方便的用选择器实现. 这些新的样式已被现代浏览器及IE9 ...