继承(Inheritance)

366 阅读9分钟

一个类可以从其他类中继承方法,属性和其他特性。当一个类继承自其他类的时候,继承的类称为子类,他继承的类称为它的父类。swift中继承是类和其他类型区分的一个基本的特性。

swift中的类可以调用和访问属于父类的方法,属性,下标并且可以提供提供这些方法,属性和下标的重写版本来重定义或者修改他们的表现。swift通过检查重写定义有匹配的父类定义帮助确认你的重写是正确的。

类也可以给继承的属性增加属性观察者来在属性的值改变的时候收到通知。属性观察自可以加给任何属性,不管它最初定义为存储或者计算属性。

定义一个基本类(Defining a Base Class)

任何不是从其他类继承的类是一个base类。

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实例,你可以访问它的description属性来打印一个人类可读的关于工具当前速度的描述:

print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour

Vehicle类为任意的一个交通工具定义了一个通用的特征,但是他自己没有使用。使他更有用,你需要对它进行重定义来描述更特殊的交通工具种类。

子类(Subclassing)

子类是在已存在的类基础上创建一个新的类。子类从已存在的类上继承特性,你可以过后重定义他们。你也可以给子类添加新的特性。

要指明子类有一个父类,在父类名称前写子类的名字,用colon分隔:

class SomeSubclass: SomeSuperclass {
    // subclass definition goes here
}

下面的例子定义了一个名为Bicycle的子类,父类为Vehicle:

class Bicycle: Vehicle {
    var hasBasket = false
}

新的Bicycle类自动获得Vehicle的全部特征,例如它的currentSpeed和description属性和它的makeNoise()方法。

除了他继承的特征,Bicycle类定义一个新的存储属性,有Basket,有一个默认的false值(推导出属性的类型为Bool)。

默认,任何你创建的新的Bicycle实例不会有一个basket。在实例创建之后你可以把特定的Bicycle实例的hasBaseket属性设置为true:

let bicycle = Bicycle()
bicycle.hasBasket = true

你也可以修改Bicycle实例的继承来的currentSpeed属性,并且查询实例的几成熟性description:

bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour

子类可以被子类。下面的例子为两个座的自行车创建了一个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: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour

重写(Overriding)

子类可以给从父类继承来的实例方法,类型方法,实例属性,类型属性,或者下标提供自定义的实现。这就是overriding。

重写继承的特征,在重写的定义前些override关键字。做了这些说明你要提供一个重写并且没有错误的提供匹配的定义。意外的重写可能导致未知的情况,任何没有override关键字的重写在代码编译的时候诊断为一个错误。

override关键字也提示了swift编译器检查你的重写类的父类(或父类之一)有匹配到你为重写提供了定义的声明。这个检查确保你的重写定义是正确的。

访问父类的方法,属性和下标(Accessing Superclass Methods,Properties,and Subscripts)

当你为子类提供了一个方法,属性,或者下标重写时,有时候需要使用已经存在的父类的实现作为重写的一部分。例如,你冲第一了一个已经存在实现的表现,或者在已经存在的继承的变量中存储了一个修改的值。

恰当的情况下,通过使用super前缀访问父类版本的方法,属性,或者下标:

  • 一个重写的名为someMethod()的方法在重写方法的实现中通过调用super.someMethod()来调用父类版本的someMethod()。
  • 一个重写的名为someProperty的属性在重写的getter或者setter实现中用super.someProperty来访问父类版本的somePropery
  • 一个重写的someIndex下标在重写下标实现中用super[someIndex]访问父类版本的下标。

重写方法(Overriding Methods)

你可以在子类中重写继承的实例或者类型方法来提供一个方法的定制的或者另外的实现。

下面的例子定义了一个新的Vehicle的名为train的子类,重写Train从Vehicle中继承的方法makeNoise():

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

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

let train = Train()
train.makeNoise()
// Prints "Choo Choo"

重写属性(Overriding Properties)

你可以重写继承的实例或者类型属性来给属性提供你自定义的getter和setter,或者增加属性的观察者使属性下面的值改变时重写的属性可以观察。

重写属性getters和setters(Overriding Property Geters and Setters)

你可以提供一个自定义的getter(和setter,如果合适)来重写任何继承的属性,不管继承的属性在来源中是不是实现为存储或者计算属性。子类不知道属性的存储或者计算性质--它只知道继承的属性有一个具体的名字和类型。你必须要声明重写属性的名字和类型,使编译器可以检查你重写的用相同的名字和类型可以匹配到父类的属性。

你可以将继承的只读属性通过在你的子类属性重写中提供getter和setter设置为读写属性。你不能,无论如何,将继承的读写属性表示为只读属性。

如果作为属性重写的一部分你提供了一个setter,你必须为那个重写提供一个getter。你过在重写getter中你不想修改继承的属性的值,你可以通过从getter中返回super.somePreperty来传出继承的值,someProperty是你重写的属性的名字。

下面的例子定义了一个名为Car的新类,是Vehicle的子类。Car类声明了一个名为gear的新的存储属性,默认整型值为1.Car类也重写了它从Vehicle中继承的description属性,来提供一个包含当前gear的自定义的description:

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

description属性的重写通过调用super.description开始,返回了Vehicle类的descritpion属性。Car类的description版本后面在description后面加入了额外的文本来提供关于当前gear的信息。

如果你创建了一个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

重写属性观察者(Overriding Property Observers)

你可以使用属性重写来给继承的属性增加属性观察者。这使你在继承的属性修改时可以得到通知,不管属性的原先实现。更多属性观察者的信息,查看Property Observers

不能给继承的常量存储属性或者继承的只读计算属性添加观察者。这些属性的值不能设置,所以不能在重写中提供willSet或者didSet的实现。注意不能给一个属性即提供重写setter也提供属性观察者。如果想观察属性值的变化,已经可以给属性提供自定义的setter,你只可以从自定义的setter中观察任何值的改变。

下面的例子定义了一个新的名为AutoMaticCar的新的类,是Car的子类。AutomaticCar类表示一个用自动变速箱的车,在当前速度自动选择合适的齿轮:

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

任何你设置一个AutomaticCar实例的currentSpeed属性,属性的didSet观察者将实例的gear属性为当前新的速度设置为合适的一个齿轮。特别的是,属性观察者选择的齿轮是新的currentSpeed的值除以10,向下取整最接近的整数,加一。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

防止重写(Preventing Overrides)

可以通过标记final来防止方法,属性,或者下标被重写。通过在方法属性,或者下标的介绍词之前写final修饰词来做到这点。(例如,finale var,final func,final class func,和final subscript)。

任何在子类中尝试重写一个final方法,属性或者下标会报一个编译时错误。在扩展中添加给一个类的方法,属性或者下标可以在扩展的定义中用final标记。

在类的定义中它的class关键字之前写final修饰词来标记整个类是final的(final class)。任何尝试给final class创建子类都会报一个编译时错误。