美文网首页
Swift编程十四(继承)

Swift编程十四(继承)

作者: 酒茶白开水 | 来源:发表于2019-06-21 17:22 被阅读0次

    案例代码下载

    继承

    类可以从另一个类继承方法,属性和其他特性。当一个类继承自另一个类时,继承类称为子类,它继承的类称为其超类。继承是一种基本行为,它将类与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
        }
    }
    

    Vehicle使用初始化程序语法创建一个新实例,该语法写为类型名称,后跟空括号:

    let someVehicle = Vehicle()
    

    创建新Vehicle实例后,可以访问其description属性以打印车辆当前速度的可读描述:

    print(someVehicle.description)
    

    在Vehicle类定义的共同特点为任意车辆,但本身没有太大用处。为了使其更有用,需要对其进行优化以描述更具体的车辆类型。

    子类

    子类化是在现有类上创建新类的行为。子类继承现有类的特征,然后可以对其进行细化。还可以向子类添加新特征。

    要指示子类具有超类,请在超类名称之前写入子类名称,用冒号分隔:

    class SomeSubclass: SomeSuperclass {
        // 子类在这里定义
    }
    

    以下示例定义了一个名为的子类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
    

    还可以修改Bicycle实例继承的一个属性currentSpeed,查询实例的继承description属性:

    bicycle.currentSpeed = 15.0
    print(bicycle.description)
    

    子类本身可以创建子类。下一个示例为Bicycle车创建一个双座自行子类,称为“tandem”:

    class Tandem: Bicycle {
        var currentNumberOfPassengers = 0
    }
    

    Tandem继承了Bicycle所有属性和方法,从而继承了Vehicle所有的属性和方法。Tandem子类还增加了一个新的存储属性currentNumberOfPassengers,用的默认值0。

    如果创建了一个Tandem实例,则可以使用其任何新的和继承的属性,并查询它继承Vehicle的只读属性description:

    let tandem = Tandem()
    tandem.hasBasket = true
    tandem.currentNumberOfPassengers = 2
    tandem.currentSpeed = 22.0
    print(tandem.description)
    

    重写

    子类可以提供自己的实例方法,类型方法,实例属性,类型属性或下标的自定义实现,否则它将从超类继承。这被称为重写。

    要覆盖否则将继承的特性,使用override关键字为覆盖定义添加前缀。这样做可以澄清打算提供覆盖并且不会提供错误的匹配定义。意外覆盖可能会导致意外行为,并且在编译代码时,没有override关键字的任何覆盖都会被诊断为错误。

    override关键字还会提示Swift编译器检查重写类的超类(或其父类之一)是否具有与重写提供的声明匹配的声明。此检查可确保您的重写定义正确无误。

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

    当为子类提供方法,属性或下标覆盖时,将现有超类实现用作覆盖的一部分有时很有用。例如,可以优化现有实现的行为,或将修改后的值存储在现有的继承变量中。

    如果这是合适的,可以使用super前缀访问方法,属性或下标的超类版本:

    • 重写方法someMethod()可以通过super.someMethod()在重写方法实现中调用超类版本someMethod()。
    • 重写载的someProperty属性可以通过super.someProperty在重写的getter或setter实现中访问的超类版本的someProperty属性。
    • 重写的someIndex下标可以通过super[someIndex]从重写的下标实现中访问同一下标的超类版本。

    重写方法

    可以覆盖继承的实例或类型方法,以在子类中提供方法的定制或替代实现。

    以下示例定义了一个新的Vehicle子类Train,Train重写了从Vehicle继承的makeNoise()方:

    class Train: Vehicle {
        override func makeNoise() {
            print("Choo Choo")
        }
    }
    

    如果创建一个Train新实例并调用其makeNoise()方法,则可以看到该方法的Train子类版本被调用:

    let train = Train()
    train.makeNoise()
    

    重写属性

    可以覆盖继承的实例或类型属性,以便为该属性提供自己的自定义getter和setter,或添加属性观察器以使覆盖属性能够在基础属性值更改时进行观察。

    覆盖属性getter和setter

    可以提供自定义getter(和setter,如果适用)以覆盖任何继承的属性,无论继承的属性是在源上实现为存储属性还是计算属性。子类不知道继承属性的存储或计算性质 - 它只知道继承的属性具有特定的名称和类型。必须始终声明要覆盖的属性的名称和类型,以使编译器能够检查的覆盖是否与具有相同名称和类型的超类属性匹配。

    通过在子类属性覆盖提供getter和setter,可以将继承的只读属性作为读写属性提供。但是,不能将继承的读写属性显示为只读属性。

    注意: 如果将setter作为属性覆盖的一部分提供,则还必须为该覆盖提供getter。如果不想在覆盖的getter中修改继承属性的值,则可以通过super.someProperty从getter 返回来简单地传递继承的值,其中someProperty是要覆盖的属性的名称。

    下面的示例定义了一个名为Car的新类,它是一个Vehicle子类。Car类定义了新的存储属性gear,用默认整数值1。Car类也覆盖了从Vehicle继承属性description,以提供定制包括当前gear的描述:

    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.description)
    

    覆盖属性观察器

    可以使用重写属性将属性观察器添加到继承的属性。这使可以在继承属性的值更改时收到通知,无论该属性最初如何实现。有关属性观察者的更多信息,请参阅Property Observers。

    注意: 不能将属性观察器添加到继承的常量存储属性或继承的只读计算属性。无法设置这些属性的值,因此不适合提供willSet或didSet实现作为覆盖的一部分。 另请注意,不能同时为同一属性提供重写setter和属性观察器。如果要观察属性值的更改,并且已经为该属性提供了自定义setter,则只需观察自定义setter中的任何值更改即可。

    下面的示例定义了一个名为AutomaticCar的新类,它是一个Car子类。所述AutomaticCar类表示用自动变速器,自动选择适当的gear适用于当前速度的一辆车:

    class AutomaticCar: Car {
        override var currentSpeed: Double {
            didSet {
                gear = Int(currentSpeed/10.0) + 1
            }
        }
    }
    

    当你设置一个AutomaticCar实例的currentSpeed属性,属性的didSet观察者设置实例的gear属性根据新速度的一个合适选择。具体来说,属性观察者选择一个新currentSpeed值除以10,向下舍入到最接近的整数,加1。35.0的速度产生gear为4:

    let automatic = AutomaticCar()
    automatic.currentSpeed = 35.0
    print(automatic.description)
    

    防止覆盖

    可以通过将方法,属性或下标标记为final来阻止它被覆盖。通过在方法,属性,或下标的申明关键字之前写final修饰符做到这一点(如final var,final func,final class func,和final subscript)。

    任何覆盖子类中的最终方法,属性或下标的尝试都会报告为编译时错误。添加到扩展中的类的方法,属性或下标也可以在扩展的定义中标记为final。

    可以通过在类定义中的class关键字之前编写final修饰符来将整个类标记为final 。任何将最终类子类化的尝试都会报告为编译时错误。

    相关文章

      网友评论

          本文标题:Swift编程十四(继承)

          本文链接:https://www.haomeiwen.com/subject/rwjuqctx.html