继承
一个类可以继承另一个类的方法,属性和其他特征。 当一个类从另一个类继承时,继承类被称为子类,并且它继承的类被称为它的超类。 继承是区分Swift中的其他类型的基本行为。
Swift中的类可以调用和访问属于它们超类的方法,属性和下标,并且可以提供它们自己的覆盖版本的这些方法,属性和下标以改进或修改它们的行为。 Swift通过检查覆盖定义是否具有匹配的超类定义来帮助确保覆盖是正确的。
类还可以将属性观察器添加到继承的属性中,以便在属性值更改时得到通知。 属性观察者可以被添加到任何属性,而不管它最初是否被定义为存储属性或计算属性。
定义一个基类
任何不从另一个类继承的类都称为基类。
注意
Swift类不会从通用基类继承。 您在未指定超类的情况下定义的类自动成为基础类,供您进行构建。
下面的例子定义了一个名为Vehicle的基类。 此基类定义一个名为currentSpeed的存储属性,默认值为0.0(推断Double属性类型)。 currentSpeed属性的值由称为description的只读计算String属性使用,以创建车辆的描述。
Vehicle基类还定义了一个名为makeNoise的方法。 此方法实际上对基础Vehicle实例不做任何操作,但稍后将由Vehicle的子类自定义:
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
使用初始值设定语句创建Vehicle的新实例,该实例的类型名称后跟空括号:
let someVehicle = Vehicle()
创建了一个新的Vehicle实例后,您可以访问其描述属性以打印车辆当前速度的可读描述:
print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
Vehicle类定义了任意车辆的共同特征,但本身并没有多大用处。 为了使其更有用,您需要对其进行改进以描述更具体的车辆类型。
子类
子类是基于现有类的新类的行为。 子类继承现有类的特征,然后可以对其进行优化。 您还可以向子类添加新的特征。
为了表明子类具有超类,请在超类名称前面写入子类名称,并用冒号分隔:
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
以下示例定义了一个名为Bicycle的子类,其中包含Vehicle的超类:
class Bicycle: Vehicle {
var hasBasket = false
}
新的Bicycle类会自动获得Vehicle的所有特性,例如其当前速度和描述属性及其makeNoise()方法。
除了它继承的特征之外,Bicycle类定义了一个新的存储属性hasBasket,其默认值为false(推断属性的Bool类型)。
默认情况下,您创建的任何新的Bicycle实例都不会有篮子。 创建该实例后,您可以将hasBasket属性设置为true:
let bicycle = Bicycle()
bicycle.hasBasket = true
您还可以修改Bicycle实例的继承的currentSpeed属性,并查询实例的继承描述属性:
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
子类本身可以被分类。 下一个例子为称为“串联”的双座自行车创建了Bicycle的子类:
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem继承了Bicycle的所有属性和方法,继而继承了Vehicle的所有属性和方法。 Tandem子类还添加了一个名为currentNumberOfPassengers的新存储属性,默认值为0。
如果您创建了Tandem实例,则可以使用它的任何新的和继承的属性,并查询它从Vehicle继承的只读描述属性:
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
重写
一个子类可以提供它自己的自定义实现,它实际上是从超类继承的实例方法,类型方法,实例属性,类型属性或下标。这被称为重写(覆盖)。
要覆盖会被继承的特性,可以使用override关键字为您的覆盖定义添加前缀。这样做明确说明您打算提供覆盖并且没有提供错误的匹配定义。意外覆盖可能会导致意外的行为,并且在编译代码时,没有覆盖关键字的覆盖将被诊断为错误。
override关键字还会提示Swift编译器检查您的重写类的超类(或其父类之一)是否具有与您为重写提供的声明相匹配的声明。此检查可确保您的覆盖定义是正确的。
访问超类方法,属性和下标
当您为子类提供方法,属性或下标覆盖时,使用现有的超类实现作为覆盖的一部分有时很有用。例如,您可以改进现有实现的行为,或将修改后的值存储在现有的继承变量中。
在适当的情况下,您可以使用 supe r前缀访问方法,属性或下标的超类版本:
重写的方法someMethod()可以通过在覆盖的方法实现中调用super.someMethod()来调用someMethod()的超类版本。
被重写的属性someProperty可以在重载的getter或setter实现中作为super.someProperty访问someProperty的超类版本。
someIndex的重写下标可以从重写的下标实现中访问与super [someIndex]相同的下标的超类版本。
重写方法
您可以重写继承的实例或类型方法,以在您的子类中提供该方法的定制或替代实现。
以下示例定义了一个名为Train的Vehicle的新子类,它覆盖了Train从Vehicle继承的makeNoise()方法:
classTrain: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
如果您创建了Train的新实例并调用了它的makeNoise()方法,您可以看到该方法的Train子类版本被调用:
let train = Train()
train.makeNoise()
// Prints "Choo Choo"
重写属性
您可以覆盖继承的实例或类型属性以为该属性提供您自己的自定义getter和setter,或者添加属性观察器以使重写属性能够观察基础属性值何时更改。
重写属性getter和setter
无论继承的属性是作为源存储还是计算属性实现的,您都可以提供自定义getter(和setter,如果适用)覆盖任何继承的属性。继承属性的存储或计算性质不为子类所知 - 它只知道继承属性具有特定的名称和类型。您必须始终声明您正在覆盖的属性的名称和类型,以使编译器能够检查您的覆盖是否与具有相同名称和类型的超类属性相匹配。
您可以通过在子类属性覆盖中提供getter和setter来呈现继承的只读属性作为读写属性。但是,您不能将继承的读写属性显示为只读属性。
注意
如果您提供一个setter作为属性重写的一部分,则还必须为该重写提供一个getter。如果您不想在重写的getter中修改继承的属性值,则可以通过从getter返回super.someProperty来传递继承的值,其中someProperty是您正在覆盖的属性的名称。
以下示例定义了一个名为Car的新类,它是Vehicle的一个子类。 Car类引入了一个名为gear的新存储属性,默认整数值为1. Car类也覆盖它从Vehicle继承的description属性,以提供包含当前齿轮的自定义描述:
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
描述属性的重写通过调用super.description开始,后者返回Vehicle类的描述属性。 Car类的说明版本会在本说明的最后添加一些额外的文字,以提供有关当前档位的信息。
如果您创建Car类的实例并设置其齿轮和currentSpeed属性,则可以看到其描述属性返回在Car类中定义的定制描述:
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
重写属性观察器
您可以使用属性重写来将属性观察器添加到继承的属性。这使您可以在继承属性的值发生更改时得到通知,而不管该属性最初是如何实现的。
注意
您不能将属性观察器添加到继承的常量存储属性或继承的只读计算属性。这些属性的值不能被设置,因此提供willSet或didSet实现作为覆盖的一部分是不合适的。
还要注意,你不能同时为同一个属性提供重写的setter和重写属性观察者。如果您想观察对属性值的更改,并且您已经为该属性提供了自定义设置器,则可以简单地观察自定义设置器中的任何值更改。
以下示例定义了一个名为AutomaticCar的新类,它是Car的一个子类。 AutomaticCar类表示带有自动变速箱的汽车,该变速箱根据当前速度自动选择适合的档位:
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
无论何时设置AutomaticCar实例的currentSpeed属性,属性的didSet观察者都会将实例的齿轮属性设置为适合新速度的齿轮选择。 具体来说,属性观察者选择一个新的当前速度值除以10的齿轮,向下舍入到最接近的整数加1.速度35.0产生齿轮4:
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
防止覆盖
您可以通过将方法,属性或脚标标记为final来防止方法,属性或脚标被覆盖。 通过在method,property或subscript的introducer关键字(如final var,final func,final class func和final subscript)之前写入最终修饰符来完成此操作。
任何试图覆盖子类中的最终方法,属性或下标的尝试都会报告为编译时错误。 您在扩展中添加到类中的方法,属性或下标也可以在扩展的定义中标记为final。
您可以通过在其类定义(final类)中的class关键字之前写入final修饰符来将整个类标记为final。 任何对最终类进行子类化的尝试都会报告为编译时错误。