一个类可以从另外一个类中继承方法,属性或者其它的一些特性。当一个类继承于另外一个类时,这个继承的类叫子类,被继承的类叫父类。继承是Swift中类区别于其它类型的一个基本特征。

Swift中的类可以调用父类的方法,使用父类的属性和下标,还可以根据需要使用重写方法或者属性来重新定义和修改他们的一些特性。Swift可以帮助你检查重写的方法和父类的方法定义是相符的。

类还可以为它继承的属性添加观察者,这样可以能够让它在一个属性变化的时候得到通知。属性观察者可以被添加给任何属性,不管它之前是存储属性还是计算属性。

1、定义一个基类

任何一个不继承于其它类的类被称作基类

注意:Swift的类不是从一个全局基类继承而来。在你编写代码的时,只要是在类的定义中没有继承自父类的类都是基类。

下面的例子定义了一个叫Vehicle的基类。基类包含两个所有交通工具通用的属性numberOfWheels和maxPassengers。这两个属性被一个叫description的方法使用,通过返回一个String描述来作为这个交通工具的特征:

class Vehicle {var numberOfWheels: Intvar maxPassengers: Intfunc description() -> String {return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"}init() {numberOfWheels = 0maxPassengers = 1}
}

这个交通工具类Vehicle还定义了一个构造函数来设置它的属性。构造函数更多的解释在Initialization一章,但是为了说明子类如何修改继承的属性,这里需要简要解释一下什么叫构造函数。

通过构造函数可以创建一个类型的实例。尽管构造函数不是方法,但是它们在编码的时候使用了非常相似的语法。构造函数通过确保所有实例的属性都是有效的来创建一个新的实例。

构造函数最简单的形式是使用init关键词的一个类似方法的函数,并且没有任何参数:

init() {// perform some initialization here
}

使用构造函数语法TypeName和空的两个小括号来完成一个Vehicle实例的创建:

let someVehicle = Vehicle()

Vehicle的构造函数为属性设置了一些初始值(numberOfWheels = 0 然后 maxPassengers = 1)。

Vehicle类定义的是一个通用的交通工具特性,它本身没有太多意义,所以就需要冲定义它的一些属性或者方法来让它具有实际的意义。

2、产生子类

产生子类就是根据一个已有的类产生新类的过程。子类继承了父类的一些可以修改的特性。还可以为子类添加一些新的特性。

为了表明一个类是继承自一个父类,需要将父类的名称写在子类的后面,并且用冒号分隔:

class SomeClass: SomeSuperclass {// class definition goes here
}

下面的例子定义了一种特定叫Bicycle的交通工具。这个新类是基于已有的类Vehicle产生的。书写方式是在类名Bicycle后加冒号加父类Vehicle名。

可以理解为:

定义一个新的类叫Bicycle,它继承了Vehicle的特性:

class Bicycle: Vehicle {init() {super.init()numberOfWheels = 2}
}

Bicycle是Vehicle的子类,Vehicle是Bicycle的父类。Bicycle类继承了Vehicle所有的特征,比如maxPassengers和numberOfWheels属性。你还可以为Bicycle类添加心的属性。

Bicycle类也定义了构造函数,在这个构造函数中调用了父类的构造函数super.init(),这样可以确保在Bicycle修改他们之前,父类已经初始化了。

注意:跟Objective-C不同的是,Swift中的构造函数没有默认继承。更多信息可以参考Initializer Inheritance and Overriding这一章节。

maxPassengers属性在继承自父类的时候已经被初始化了,对于Bicycle来说是正确的,因此不需要再做更改。然后numberOfWheels是不对的,所以被替换成了2.

不仅属性是继承于Vehicle的,Bicycle还继承了父类的方法。如果你创建一个实例,然后调用了已经继承的description方法,可以得到该交通工具的描述并且看到它的属性已经被修改:

let bicycle = Bicycle()
println("Bicycle: \(bicycle.description())")
// Bicycle: 2 wheels; up to 1 passengers

子类本身也可以作为父类被再次继承:

class Tandem: Bicycle {init() {super.init()maxPassengers = 2}
}

上面的例子创建了Bicycle的子类,叫做tandem,也就可以两个人一起骑的自行车。所以Tandem没有修改numberOfWheels属性,只是更新了maxPassengers属性。

注意:子类只能够在构造的时候修改变量的属性,不能修改常量的属性。

创建一个Tandem的实例,然后调用description方法检查属性是否被正确修改:

let tandem = Tandem()
println("Tandem: \(tandem.description())")
// Tandem: 2 wheels; up to 2 passengers

注意到description方法也被Tandem继承了。

3、重写方法

子类可以提供由父类继承来的实例方法,类方法,实例属性或者下标的个性化实现。这个特性被称为重写。

重写一个由继承而来的方法需要在方法定义前标注override关键词。通过这样的操作可以确保你所要修改的这个方法确实是继承而来的,而不会出现重写错误。错误的重写会造成一些不可预知的错误,所以如果如果不标记override关键词的话,就会被在代码编译时报错。

override关键词还能够让Swift编译器检查该类的父类是否有相符的方法,以确保你的重写是可用的,正确的。

访问父类方法,属性和下标

当在重写子类继承自父类的方法,属性或者下标的时候,需要用到一部分父类已有的实现。比如你可以重定义已知的一个实现或者在继承的变量中存储一个修改的值。

适当的时候,可以通过使用super前缀来访问父类的方法,属性或者下标:

叫someMethod的重写方法可以在实现的时候通过super.someMethod()调用父类的someMethod方法。

叫someProperty的重写属性可以在重写实现getter或者setter的时候通过super.someProperty调用父类的someProperty。

叫someIndex的重写下标可以在实现下标的时候通过super[someIndex]来访问父类的下标。

复写方法

你可以在你的子类中实现定制的继承于父类的实例方法或者类方法。

下面的例子演示的就是一个叫Car的Vehicle子类,重写了继承自Vehicle的description方法。

class Car: Vehicle {var speed: Double = 0.0init() {super.init()maxPassengers = 5numberOfWheels = 4}override func description() -> String {return super.description() + "; "+ "traveling at \(speed) mph"}
}

Car中定义了一个新的Double类型的存储属性speed。这个属性默认值是0.0,意思是每小时0英里。Car还有一个自定义的构造函数,设置了最大乘客数为5,轮子数量是4.

Car重写了继承的description方法,并在方法名description前标注了override关键词。

在description中并没有给出了一个全新的描述实现,还是通过super.description使用了Vehicle提供的部分描述语句,然后加上了自己定义的一些属性,如当前速度。

如果你创建一个Car的实例,然后调用description方法,会发现描述语句变成了这样:

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

复写属性

你还可以提供继承自父类的实例属性或者类属性的个性化getter和setter方法,或者是添加属性观察者来实现重写的属性可以观察到继承属性的变动。

重写属性的Getters和Setters

不管在源类中继承的这个属性是存储属性还是计算属性,你都可以提供一个定制的getter或者setter方法来重写这个继承属性。子类一般不会知 道这个继承的属性本来是存储属性还是计算属性,但是它知道这个属性有特定的名字和类型。在重写的时候需要指明属性的类型和名字,好让编译器可以检查你的重 写是否与父类的属性相符。

你可以将一个只读的属性通过提那家getter和setter继承为可读写的,但是反之不可。

注意:如果你为一个重写属性提供了setter方法,那么也需要提供getter方法。如果你不想在getter中修改继承的属性的值,可以在getter中使用super.someProperty即可,在下面SpeedLimitedCar例子中也是这样。

下面的例子定义了一个新类SpeedLimitedCar,是Car的一个子类。这个类表示一个显示在40码一下的车辆。

通过重写继承的speed属性来实现:

class SpeedLimitedCar: Car {override var speed: Double  {get {return super.speed}set {super.speed = min(newValue, 40.0)}}
}

每当你要设置speed属性的时候,setter都会检查新值是否比40大,二者中较小的值会被设置给SpeedLimitedCar。

如果你尝试为speed设置超过40的值,description的输出依然还是40:

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

重写属性观察者

你可以使用属性重写为继承的属性添加观察者。这种做法可以让你无论这个属性之前是如何实现的,在继承的这个属性变化的时候都能得到提醒。更多相关的信息可以参考Property Observers这章。

注意:不能为继承的常量存储属性或者是只读计算属性添加观察者。这些属性值是不能被修改的,因此不适合在重写实现时添加willSet或者didSet方法。

注意:不能同时定义重写setter和重写属性观察者,如果想要观察属性值的变化,并且又为该属性给出了定制的setter,那只需要在setter中直接获得属性值的变化就行了。

下面的代码演示的是一个新类AutomaticCar,也是Car的一个子类。这个类表明一个拥有自动变速箱的汽车,可以根据现在的速度自动选择档位,并在description中输出当前档位:

class AutomaticCar: Car {var gear = 1override var speed: Double {didSet {gear = Int(speed / 10.0) + 1}}override func description() -> String {return super.description() + " in gear \(gear)"}
}

这样就可以实现,每次你设置speed的值的时候,didSet方法都会被调用,来看档位是否需要变化。gear是由speed除以10加1计算得来,所以当速度为35的时候,gear档位为4:

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

4、禁止重写

你可以通过标记final关键词来禁止重写一个类的方法,属性或者下标。在定义的关键词前面标注@final属性即可。

在子类中任何尝试重写父类的final方法,属性或者下标的行为都会在编译时报错。同样在扩展中为类添加的方法,属性或者下标也可以被标记为final。

还可以在类关键词class前使用@final标记一整个类为final(@final class)。任何子类尝试继承这个父类时都会在编译时报错。

感谢翻译小组成员:李起攀(微博)、若晨(微博)、YAO、粽子、山有木兮木有枝、渺-Bessie、墨离、矮人王、CXH、Tiger大顾(微博)
个人转载请注明出处和原始链接http://letsswift.com/2014/06/inheritance,商业转载请联系我们~ 感谢您对我们工作的支持~

Swift中文教程(十三) 继承相关推荐

  1. Swift中文教程(五)--对象和类

    原文:Swift中文教程(五)--对象和类 Class 类 在Swift中可以用class关键字后跟类名创建一个类.在类里,一个属性的声明写法同一个常量或变量的声明写法一样,除非这个属性是在类的上下文 ...

  2. Swift中文教程(三)--流程控制

    Swift中文教程(三)--流程控制 原文:Swift中文教程(三)--流程控制 Swift用if和switch编写条件控制语句,用for-in,for,while和do-while编写循环.条件控制 ...

  3. Swift中文教程(二十三) 高级运算符

    除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算. 不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的.溢出行为会 ...

  4. Swift中文教程(二十二) 泛型

    泛型代码可以让你写出根据自我需求定义.适用于任何类型的,灵活且可重用的函数和类型.它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图. 泛型是 Swift 强大特征中的其中一个,许多 ...

  5. Swift中文教程(二十一) 协议

    Protocol(协议)用于统一方法和属性的名称,而不实现任何功能.协议能够被类,枚举,结构体实现,满足协议要求的类,枚举,结构体被称为协议的遵循者. 遵循者需要提供协议指定的成员,如属性,方法,操作 ...

  6. Swift中文教程(十五) 析构

    在一个类的实例被释放之前,析构函数会被调用.用关键字deinit来定义析构函数,类似于初始化函数用init来定义.析构函数只适用于class类型. 1.析构过程原理 Swift 会自动释放不再需要的实 ...

  7. Swift中文教程(十四) 初始化

    初始化是类,结构体和枚举类型实例化的准备阶段.这个阶段设置这个实例存储的属性的初始化数值和做一些使用实例之前的准备以及必须要做的其他一些设置工作. 通过定义构造器(initializers)实现这个实 ...

  8. Swift中文教程(十) 属性

    属性是描述特定类.结构或者枚举的值.存储属性作为实例的一部分存储常量与变量的值,而计算属性计算他们的值(不只是存储).计算属性存在于类.结构与枚举中.存储属性仅仅只在类与结构中. 属性通常与特定类型实 ...

  9. Swift中文教程(九) 类与结构

    类与结构是编程人员在代码中会经常用到的代码块.在类与结构中可以像定义常量,变量和函数一样,定义相关的属性和方法以此来实现各种功能. 和其它的编程语言不太相同的是,Swift不需要单独创建接口或者实现文 ...

最新文章

  1. linux下如何将mysql加入环境变量
  2. backboneJs 导图
  3. 计算机网络基础 — 网络的类型
  4. 5道Python函数练习
  5. docker 的资源控制和数据管理
  6. 长期支持对OpenJDK意味着什么?
  7. RF新手常见问题总结--(基础篇)
  8. Redis一个命令请求从发送到完成的步骤以及初始化服务器步骤
  9. php获取mysql所有表_PHP获取MySQL数据库里所有表的实现代码
  10. 信息学奥赛一本通 1308:【例1.5】高精除
  11. python函数中变量的作用域_Python中变量的作用域(variable scope)
  12. MAC地址_IP地址
  13. Hadoop架构与原理:Hadoop系统架构原理解析
  14. WIFI小车记录四:用Adobe Illustrator绘制应用磁贴
  15. 今天小暑是什么时间_小暑时间是什么?代表什么?
  16. 虾皮有哪些站点?各站点有什么特色
  17. 用Matlab的FDAtool生成IIR滤波器参数
  18. 我要你觉得,我不要我觉得--根据企业现状实施DevOps
  19. 2023年安徽省中职网络安全跨站脚本攻击
  20. Dolby技术汇总(二) --- Dolby Mobile

热门文章

  1. 不与最大数相同的数字之和(信息学奥赛一本通-T1113)
  2. 3 MIGO采购订单收货报错-表T169P,表目S001不存在
  3. virtualbox macos_MacOS 终于可以完美使用 Podman 了!
  4. 笨方法“学习python笔记之变量及打印
  5. VMware Tools显示灰色的办法
  6. Charbonnier Loss
  7. vue组件的通信方式
  8. phpcms关于 {if} 判断后台是否上传{thumb} 缩略图 - 代码篇
  9. vue.js踩坑之组件参数检验与非props特性
  10. 呆错网址导航系统V1.13.18 PHP源码