Swift5.4 语言指南(十五) 继承
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9739308.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
一个类可以从另一个类继承方法,属性和其他特征。当一个类从另一个类继承时,继承的类称为子类,而其继承的类称为其超类。继承是一种基本行为,可将Swift中的类与其他类型区分开。
Swift中的类可以调用和访问属于其父类的方法,属性和下标,并可以提供这些方法,属性和下标的自己的重写版本以完善或修改其行为。Swift通过检查覆盖定义是否具有匹配的超类定义来帮助确保覆盖是正确的。
类还可以将属性观察器添加到继承的属性中,以便在属性值更改时得到通知。可以将属性观察器添加到任何属性,而不管它最初是被定义为存储属性还是计算属性。
定义基类
任何不从另一个类继承的类都称为基类。
笔记
Swift类不从通用基类继承。您未指定超类而定义的类将自动成为基础类供您构建。
下面的示例定义了一个名为的基类Vehicle
。该基类定义了一个名为的存储属性currentSpeed
,其默认值为0.0
(推断属性类型为Double
)。该currentSpeed
属性的值由String
调用的只读计算属性使用,description
以创建车辆的描述。
所述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
使用Initializer语法创建一个新的实例,该实例以类型名称和空括号的形式编写:
- let someVehicle = Vehicle()
创建新Vehicle
实例后,您可以访问其description
属性以打印出人类对车辆当前速度的描述:
- 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
,例如currentSpeed
和description
属性以及makeNoise()
方法。
除了继承的特性外,Bicycle
该类还定义了一个新的存储属性,hasBasket
其默认值为false
(推断Bool
该属性的类型)。
默认情况下,Bicycle
您创建的任何新实例都不会包含篮子。您可以设置hasBasket
属性true
为特定Bicycle
的创建实例之后,例如:
- let bicycle = Bicycle()
- bicycle.hasBasket = true
您还可以修改继承的currentSpeed
一个财产Bicycle
实例,查询实例的继承description
财产:
- 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
,则可以使用其任何新属性和继承的属性,并查询description
其继承自的只读属性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
在编译代码时,任何没有关键字的覆盖都会被诊断为错误。
该override
关键字还提示斯威夫特编译器检查您的覆盖类的超(或父母中的一方)有符合您的覆盖提供的一个声明。此检查可确保您的覆盖定义正确。
访问超类的方法,属性和下标
当为子类提供方法,属性或下标替代时,将现有的超类实现用作替代的一部分有时会很有用。例如,您可以优化该现有实现的行为,或者将修改后的值存储在现有的继承变量中。
在适当的情况下,您可以使用super
前缀访问方法,属性或下标的超类版本:
- 名为的重写方法
someMethod()
可以someMethod()
通过super.someMethod()
在重写方法实现中调用来调用的超类版本。 - 被称为覆盖的属性
someProperty
可以访问的超类版本someProperty
作为super.someProperty
压倒一切的getter或setter实现中。 - 的覆盖下标
someIndex
可以从覆盖下标实现中访问同一下标的超类版本super[someIndex]
。
覆盖方法
您可以重写继承的实例或类型方法,以在子类中提供该方法的定制或替代实现。
以下示例定义了一个Vehicle
名为的新子类Train
,该子类将覆盖从其继承的makeNoise()
方法:Train
Vehicle
- class Train: Vehicle {
- override func makeNoise() {
- print("Choo Choo")
- }
- }
如果创建的新实例Train
并调用其makeNoise()
方法,则可以看到该方法的Train
子类版本被调用:
- let train = Train()
- train.makeNoise()
- // Prints "Choo Choo"
覆盖属性
您可以覆盖继承的实例或类型属性,以为该属性提供自己的自定义getter和setter,或添加属性观察器以使覆盖的属性能够在基础属性值更改时进行观察。
覆盖属性获取器和设置器
您可以提供一个自定义的getter(如果合适的话,可以使用setter)来覆盖任何继承的属性,而不管该继承的属性是在源上实现为存储属性还是计算属性。子类不知道继承属性的存储或计算性质,它仅知道继承属性具有特定的名称和类型。您必须始终声明要覆盖的属性的名称和类型,以使编译器能够检查您的覆盖是否与具有相同名称和类型的超类属性匹配。
通过在子类属性重写中同时提供getter和setter,可以将继承的只读属性呈现为读写属性。但是,您不能将继承的读写属性表示为只读属性。
笔记
如果在属性替代中提供了一个setter,则还必须为该替代提供一个getter。如果您不想在覆盖的getter中修改继承的属性的值,则可以简单地通过super.someProperty
从getter返回值来传递继承的值,其中someProperty
您要覆盖的属性的名称是。
以下示例定义了一个名为的新类Car
,它是的子类Vehicle
。该Car
课程介绍了新的存储属性调用gear
,用默认整数值1
。的Car
类也覆盖了description
它从继承属性Vehicle
,以提供包括当前齿轮定制描述:
- class Car: Vehicle {
- var gear = 1
- override var description: String {
- return super.description + " in gear \(gear)"
- }
- }
description
通过调用super.description
,开始属性的重写,该方法返回Vehicle
类的description
属性。然后,Car
该类的版本description
在此描述的末尾添加一些额外的文本,以提供有关当前装备的信息。
如果创建Car
该类的实例并设置其gear
和currentSpeed
属性,则可以看到其description
属性返回在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
实现作为替代的一部分提供。
还要注意,您不能同时为同一属性提供覆盖的设置器和覆盖的属性观察器。如果您想观察属性值的变化,并且已经在为该属性提供自定义设置器,则可以简单地观察自定义设置器中的任何值更改。
以下示例定义了一个名为的新类AutomaticCar
,它是的子类Car
。所述AutomaticCar
类表示用自动变速器,自动选择适当的齿轮基于当前速度使用一辆车:
- class AutomaticCar: Car {
- override var currentSpeed: Double {
- didSet {
- gear = Int(currentSpeed / 10.0) + 1
- }
- }
- }
当你设置currentSpeed
一个的属性AutomaticCar
实例属性的didSet
观察者设置实例的gear
属性齿轮的新速度一个合适的选择。具体来说,属性观察者选择的齿轮是新currentSpeed
值除以10
,并四舍五入为最接近的整数plus 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来防止其被覆盖。通过写做到这一点final
的方法,属性,或标的介绍人关键字之前修饰符(如,,,和)。final var
final func
final class func
final subscript
尝试覆盖子类中的最终方法,属性或下标的任何尝试都将报告为编译时错误。您在扩展的类中添加的方法,属性或下标也可以在扩展的定义内标记为final。
您可以通过在类定义()中final
的class
关键字之前编写修饰符来将整个类标记为最终类。任何试图将最终类作为子类的尝试都将报告为编译时错误。final class