swift类继承详解

分享到:

一个类可继承另一个类的方法、属性以及其他特点。

当一个类继承其它类,继承类叫子类,被继承类叫超类(或父类)。在Swift中,继承是区分类与其它类型的一个基本特征。

在Swift中的类可以调用和访问超类的方法、属性和下标,并且可以重写这些方法、属性和下标来优化或修改它们的行为。Swift通过检查重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。

可以为类中继承来的属性添加属性观察者,而当属性值改变时,类就会被通知到。可以为任何属性添加属性观察者,无论它原本被定义为存储型属性还是计算型属性。

 

定义一个基类

 

不继承其它类的任意类被称为基类。

注:Swift中的类并不是从一个通用的基类继承而来。若未能为你定义的类指定一个超类,这个类则自动成为基类。

下述示例定义了名为vehicle的基类。这个基类声明了两个对所有车辆均通用的属性(numberOfWheels和maxPassengers)。这些属性在description方法中使用,这个方法返回一个String类型的,对车辆特征的描述。

 

class Vehicle {

var numberOfWheels: Int

var maxPassengers: Int

func description() -> String {

return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"

}

init() {

numberOfWheels = 0

maxPassengers = 1

 

在Vehicle类中还定义了一个初始化器设置其属性。初始化器会在初始化中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性可以被如何修改。

初始化器用于创建某个类型的一个新实例。尽管初始化器不是方法,但是他们被以相同的语法写入实例方法中。初始化器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。

初始化器的最简单形式就像一个没有参数的实例方法,使用关键字init写入:

 

init() {

// perform some initialization here

}

 

如果要创建一个Vehicle类的新实例,使用初始化器语法调用上面的初始化器,即在TypeName后写入一个空括号:

 

let someVehicle = Vehicle()

 

这个Vehicle类的初始化器为任意的一辆车设置一些初始化属性值(numberOfWheels = 0和maxPassengers =1)。

Vehicle类定义了车辆的共同特性,但这个类本身并没太大用处。为了让其更实用,你需要进一步细化它来描述更具体的车辆。

 

子类化

 

Subclassing指的是在一个已有类的基础上创建一个新的类。子类继承现有类的所有属性,你可以细化这些属性。你还可以在子类中添加新的属性。 为了指明某个类的超类,将超类名写在子类名的后面,用冒号分:

 

class SomeClass: SomeSuperclass {

// class definition goes here

}

 

下一个示例中定义了一个更具体的车辆类Bicycle。这个新类是在Vehicle类的基础上创建起来。因此你需要将Vehicle类放在Bicycle类后面,用冒号分隔。

我们可将其读作:

“定义一个名为Bicycle的新类,此新类可继承Vehicle的全部属性”:

 

class Bicycle: Vehicle {

init() {

super.init()

numberOfWheels = 2

}

}

 

Bicycle是Vehicle的一个子类,而Vehicle是Bicycle的超类。新的Bicycle自动获得Vehicle的所有属性,例如其maxPassengers和numberOfWheefe属性。可以在子类中定制此类属性,或添加新的属性以便更好地描述Bicycle类。

Bicycle类定义了一个初始化器来设置它定制的特性。 Bicycle的初始化器调用了其超类Vehicle的初始化器superinit,以此确保在Bicycle类试图修改那些继承来的属性前,Vehicle类已将其初始化。

注:不同于Objective-C,在Swift中,默认情况下不能继承初始化器。更多相关信息,参见初始化器的继承和重写。

Vehicle类中maxPassengers的默认值对自行车而言是正确的,因此在Bicycle的初始化器中并没有对其进行更改。而numberOfWheels原值是不正确的,因此在初始化器中将它更改为新值2。

Bicycle不仅可以继承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从Bicycle继承了两个属性,而这两个属性是Bicycle从Vehicle继承而来的。Tandem并不修改轮子的数量,毕竟它仍是一辆自行车。但它需要修改maxPassengers的值,因为双人自行车可以坐两个人。

注:子类只允许修改从超类继承来的变量属性。而无法修改继承的常量属性。

创建一个Tandem实例,打印它的描述,即可看到它的属性已被更新:

 

let tandem = Tandem()

println("Tandem: \(tandem.description())")

// Tandem: 2 wheels; up to 2 passengers

 

注:descripton方法也可以由Tandem继承。类的实例方法可以由全部子类继承。

 

重写

 

子类可以为继承来的实例方法、类方法、实例属性或其另外从一个超类处继承的下标自定义实现。我们把这种行为称为重写。

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

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

访问超类的方法、属性和下标

 

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

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

重写方法someMethod可以在重写方法实现内通过调用super.someMethod()来调用someMethod的超类版本。

重写属性someProperty可在getter或setter实现内将someProperty超类版本存储为super.someProperty。

An overridden subscript for someIndex的重写下标可在重写下标内将同一下标的超类版本存储为super[someIndex]

 

重写方法

 

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

下列示例中定义了被称为Car的Vehicle的一个新子类,该子类重写其从Vehicle继承的description方法:

 

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 Car声明了一个名为speed的新存储Double属性。此属性默认值0.0,表示“时速是0英里”。Car有自己的初始化器,该初始化器把乘客最大数量设置到5,车轮默认数量为4.

按来自从Vehicle的descripton方法,通过用相同的描述提供一个方法,car重写其继承的descripton方法。重写方法定义以关键字override作为前缀。

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

如果你创建一个Car的新实例,并且打印descripton方法输出,你可以看到描述信息已经被更改:

 

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即可。但是,你不能将一个继承来的读写属性重写为一个只读属性。

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

 

class SpeedLimitedCar: Car {

override var speed: Double {

get {

return super.speed

}

set {

super.speed = min(newValue, 40.0)

}

}

 

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

若你尝试设置一个SpeedLimitedCar的speed属性超过40mph,然后打印其descripton方法输出,你会发现该速度被限制在:40mph

 

let limitedCar = SpeedLimitedCar()

limitedCar.speed = 60.0

println("SpeedLimitedCar: \(limitedCar.description())")

// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph

 

重写属性观察者

 

你可以在属性重写中为一个继承来的属性添加属性观察者。当继承属性值改变时,你就会被通知到,无论那个属性原本是如何实现的。更多关于属性观察者的信息,参阅属性观察者。

注:禁止为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察者。这些属性值是不可以被设置的,因此,不应为其重写提供willSet或didSet。 此外还要注,你不可以同时提供重写的setter和重写的属性观察者。若想观察属性值的变化,并且你已经为那个属性提供了定制的setter,你在setter中则可以观察到任何值变化了。

以下示例中定义一个被称为AutomaticCar的新类,它是Car的一个子类。AutomaticCar类表明汽车带有自动变速器,该变速器能根据当前速度自动选择一个合适的档位。AutomaticCar还提供了定制的descripton方法,可以输出当前档位。

 

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观察者为新的速度自动设置档位属性至一个合适的档位。具体来说,属性观察者将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位gear的值。例如,速度为10.0时,档位为1;速度为35.0时,档位为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
IOS实例
ObjectiveC