Swift继承的用法
一个类可以继承另一个类的方法,属性和其它特性。当一个类继承其它类,继承类叫子类,被继承类叫超类(或父类)。在Swift中,继承是区分「类」与其它类型的一个基本特征。
在Swift中,类可以调用和访问超类的方法,属性和下标,并且可以重写(override)这些方法,属性和下标来优化或修改它们的行为。Swift会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
可 以为类中继承来的属性添加属性观察器(property observer),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
定义一个基类
不继承于其它类的类,称之为基类。
注意:Swift中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
下面的例子定义了一个叫Vehicle的基类。这个基类声明了两个对所有车辆都通用的属性(numberOfWheels和maxPassengers)。这些属性在description方法中使用,这个方法返回一个String类型的,对车辆特征的描述:
1
2
3
4
5
6
7
8
9
10
11
|
class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" } init() { numberOfWheels = 0 maxPassengers = 1 } } |
Vehicle类定义了初始化器(initializer)来设置属性的值。初始化器会在构造函数一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性可以如何被修改。
初始化器用于创建某个类型的一个新实例。尽管初始化器并不是方法,但在语法上,两者很相似。初始化器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
初始化器的最简单形式就像一个没有参数的实例方法,使用init关键字:
1
2
3
|
init ( ) { / / perform some initialization here } |
如果要创建一个Vehicle类的新实例,使用初始化器语法调用上面的初始化器,即类名后面跟一个空的小括号:
1
|
let someVehicle = Vehicle ( ) |
这个Vehicle类的初始化器为任意的一辆车设置一些初始化属性值(numberOfWheels = 0和maxPassengers = 1)。
Vehicle类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。
Subclassing(子类化?待定)
subclassing指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且你可以优化或改变它。你还可以为子类添加新的特性。
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
1
2
3
|
class SomeClass : SomeSuperclass { / / class definition goes here } |
下一个例子,定义一个更具体的车辆类叫Bicycle。这个新类是在Vehicle类的基础上创建起来。因此你需要将Vehicle类放在Bicycle类后面,用冒号分隔。
我们可以将这读作:
“定义一个新的类叫Bicycle,它继承了Vehicle的特性”;
1
2
3
4
5
6
|
class Bicycle : Vehicle { init ( ) { super.init ( ) numberOfWheels = 2 } } |
Bicycle 是Vehicle的子类,Vehicle是Bicycle的超类。新的Bicycle类自动获得Vehicle类的特性,比如 maxPassengers和numberOfWheels属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述Bicycle类。
Bicycle类定义了一个初始化器来设置它定制的特性(自行车只有2个轮子)。Bicycle的初始化器调用了它父类Vehicle的初始化器super.init(),以此确保在Bicycle类试图修改那些继承来的属性前,Vehicle类已经初始化过它们了。
注意:不像Objective-C,在Swift中,初始化器默认是不继承的,见初始化器的继承与重写
Vehicle类中maxPassengers的默认值对自行车来说已经是正确的,因此在Bicycle的初始化器中并没有改变它。而numberOfWheels原来的值对自行车来说是不正确的,因此在初始化器中将它更改为2。
Bicycle不仅可以继承Vehicle的属性,还可以继承它的方法。如果你创建了一个Bicycle类的实例,你就可以调用它继承来的description方法,并且可以看到,它输出的属性值已经发生了变化:
1
2
3
|
let bicycle = Bicycle ( ) println ( "Bicycle: \(bicycle.description())" ) / / Bicycle : 2 wheels; up to 1 passengers |
子类还可以继续被其它类继承:
1
2
3
4
5
6
|
class Tandem : Bicycle { init ( ) { super.init ( ) maxPassengers = 2 } } |
上 面的例子创建了Bicycle的一个子类:双人自行车(tandem)。Tandem从Bicycle继承了两个属性,而这两个属性是 Bicycle从Vehicle继承而来的。Tandem并不修改轮子的数量,因为它仍是一辆自行车,有2个轮子。但它需要修改 maxPassengers的值,因为双人自行车可以坐两个人。
注意:子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。
创建一个Tandem类的实例,打印它的描述,即可看到它的属性已被更新:
1
2
3
|
let tandem = Tandem ( ) println ( "Tandem: \(tandem.description())" ) / / Tandem : 2 wheels; up to 2 passengers |
注意,Tandem类也继承了description方法。一个类的实例方法会被这个类的所有子类继承。
重写(Overriding)
子 类可以为继承来的实例方法(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
2
3
4
5
6
7
8
9
10
11
12
|
class Car : Vehicle { var speed : Double = 0.0 init ( ) { super.init ( ) maxPassengers = 5 numberOfWheels = 4 } override func description ( ) - > String { return super.description ( ) + "; " + "traveling at \(speed) mph" } } |
Car声明了一个新的存储型属性speed,它是Double类型的,默认值是0.0,表示“时速是0英里”。'Car'有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。
Car重写了继承来的description方法,它的声明与Vehicle中的description方法一致,声明前面加上了override关键字。
Car中的description方法并非完全自定义,而是通过super.description使用了超类Vehicle中的description方法,然后再追加一些额外的信息,比如汽车的当前速度。
如果你创建一个Car的新实例,并打印description方法的输出,你就会发现描述信息已经发生了改变:
1
2
3
|
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里修改继承来的属性值,你可以直接返回super.someProperty来返回继承来的值。正如下面的SpeedLimitedCar的例子所示。
以下的例子定义了一个新类,叫SpeedLimitedCar,它是Car的子类。类SpeedLimitedCar表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的speed属性来实现这个速度限制:
1
2
3
4
5
6
7
8
9
10
|
class SpeedLimitedCar : Car { override var speed : Double { get { return super.speed } set { super.speed = min ( newValue , 40.0 ) } } } |
当 你设置一个SpeedLimitedCar实例的speed属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的 speed设置为newValue和40.0中较小的那个。这两个值哪个较小由min函数决定,它是Swift标准库中的一个全局函数。min函数接收两 个或更多的数,返回其中最小的那个。
如果你尝试将SpeedLimitedCar实例的speed属性设置为一个大于40mph的数,然后打印description函数的输出,你会发现速度被限制在40mph:
1
2
3
4
|
let limitedCar = SpeedLimitedCar ( ) limitedCar.speed = 60.0 println ( "SpeedLimitedCar: \(limitedCar.description())" ) / / 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
2
3
4
5
6
7
8
9
10
11
|
class AutomaticCar : Car { var gear = 1 override var speed : Double { didSet { gear = Int ( speed / 10.0 ) + 1 } } override func description ( ) - > String { return super.description ( ) + " in gear \(gear)" } } |
当 你设置AutomaticCar的speed属性,属性的didSet观察器就会自动地设置gear属性,为新的速度选择一个合适的挡位。具体来 说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位gear的值。例如,速度为10.0时,挡位为1;速度为 35.0时,挡位为4:
1
2
3
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 |
防止重写
你可以通过把方法,属性或下标标记为final来防止它们被重写,只需要在声明关键字前加上@final特性即可。(例如:@final var, @final func, @final class func, 以及 @final subscript)
如果你重写了final方法,属性或下标,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标也可以在扩展的定义里标记为final。
你可以通过在关键字class前添加@final特性(@final class)来将整个类标记为final的,这样的类是不可被继承的,否则会报编译错误。
Swift继承的用法的更多相关文章
- Swift—继承
一个类可以继承另一个类的方法,属性和其他特性.当一个类继承其他类时,继承类叫子类,被继承类叫超类(或父类).在Swift中,继承具有单继承的特点,每个子类只有一个直接父类,继承是区分类与其他类型的一个 ...
- 学习Swift -- 继承
继承 一个类可以继承另一个类的方法(methods),属性(properties)和其它特性.当一个类继承其它类时,继承类叫子类,被继承类叫超类(父类). 在 Swift 中,子类可以调用和访问父类的 ...
- Swift: 继承
为了在属性值改变的时候获得通知,类可以为继承的属性添加属性观察者.属性观察者可以添加到任何属性上,不管这个属性原来是存储属性还是计算属性. Swift中的类没有一个统一的基类. 为了讲明白继承,我们先 ...
- Swift - 继承UIView实现自定义可视化组件(附记分牌样例)
在iOS开发中,如果创建一个自定义的组件通常可以通过继承UIView来实现.下面以一个记分牌组件为例,演示了组件的创建和使用,以及枚举.协议等相关知识的学习. 效果图如下: 组件代码:Score ...
- swift 继承和构造器
继承 class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { ...
- Swift - enumerateObjectsUsingBlock的用法
enumerateobjectsusingblock:不是Array的方法在NSArray使用.如果你想使用它,你需要一个实例NSArray而不是Array. import Foundation va ...
- swift函数的用法,及其嵌套实例
import Foundation //swift函数的使用 func sayHello(name userName:String ,age:Int)->String{ return " ...
- C#继承的用法
using System; namespace 继承 { public class cat { private string _name = null; private int _age = 0; p ...
- OpenERP里面继承的用法
最近开发遇到了这样的问题:需要往HR模块里面添加一些查询条件,这些查询条件是HR模型里已经写好的,直接修改HR肯定可以实现,但是HR模块一旦修改就会导致一系列的错误,OE开发中的一项基本原则就是不可修 ...
随机推荐
- extjs tree check 级联选择
extjs4 tree check 级联选择 实现效果: 关键代码: function changeAllNode(node, isCheck) { allChild(node, isCheck); ...
- unity3d 建树篇
今天碰到有人问这个问题,然后我经过一番折腾,找到了方法.例如以下: 有学过Unity3d的同学生都知道我们在对地形拖拉树木等表层时,其树木在我们实例执行中,它们都是能够任其他物体穿过. 这是为什么.相 ...
- 面试前的准备---C#知识点回顾----03
经过一天的奔波,喜忧参半,不细表 再回看下标题,C#知识点回顾 再看下内容,数据库3NF 原谅我这个标题党 今天继续回忆 1.HTTP中Post和Get区别 这忒简单了吧,大家是不是感觉到兴奋了,长舒 ...
- JS中的函数节流
函数节流的目的 从字面上就可以理解,函数节流就是用来节流函数从而一定程度上优化性能的.例如,DOM 操作比起非DOM 交互需要更多的内存和CPU时间.连续尝试进行过多的DOM 相关操作可能会导致浏览器 ...
- js数组去重,并统计最多项算法
从事了一段时间的前端开发,今天写了一个数组去重,并统计最多项的方法,目前只支持数组的项都是数字. 由于本人能力有限,希望能得到网友的指正!如有问题或者更好的实现思路,也欢迎大家和我讨论!代码如下: f ...
- IOS 中关于自定义Cell 上的按钮 开关等点击事件的实现方法(代理)
1.在自定义的Cell .h文件中写出代理,写出代理方法. @protocol selectButtonDelegate <NSObject> -(void)selectModelID:( ...
- 13年山东省赛 The number of steps(概率dp水题)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud The number of steps Time Limit: 1 Sec Me ...
- (php)生成指定个数的随机红包
<?php $total=20;//红包总金额 $num=10;// 分成10个红包,支持10人随机领取 $min=0.01;//每个人最少能收到0.01元 $redpack = new red ...
- 如何成为一个真正在路上的Linuxer
Linux 是工具,却更像一个信仰. 写在前面: 本文目的不是教你如何成为一个真正的Linuxer,也没有能力教你成为一个真正的linuxer,而是通过笔者的一些想法试图指引你真正踏上学习linux之 ...
- Django学习(五) 定义视图以及页面模板
请求解析一般都是通过请求的request获取一定参数,然后根据参数做一定业务逻辑判断,这其中可能包括查询数据库,然后将需要返回的数据封装成一个HttpResponse返回. 代码如下: 这是一个简单的 ...