继承
继承
类可以继承另一个类的方法、属性和其他特性。一个继承自其他类的类叫做子类(subclass),被继承的类叫做超类(superclass)。继承是一个基础行为,是类和其他类型不同点,在Swift中。
Swift中的类,可以调用和访问超类的方法、属性和下标,还可以提供上述方法、属性和下标的自身重写版本。Swift会帮助检查确保子类的重载兼容超类的定义。
类中可以给继承的来的属性添加观察者,为了在属性被修改时获取通知。属性的观察者可以被添加在任何的属性上,不管这个属性原始定义是存储型的还是计算型的。
定义一个基本类(Defining a Base Class)
任何一个不继承其他类的类是一个基本类(Base Class)
NOTE
Swift的类没有共有的超类。(译者注:和java不同)没有指定超类的类都会自动按照基本类对待。
下面的例子定义了一个基本类叫做Vehicle。这个基本类定义了一个存储属性叫做currentSpeed,currentSpeed有默认值0.0(据此推断这个属性的类型是Double)。currentSpeed属性的值被只读的String类型的计算属性description使用,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 } }
你使用初始化语法(initializer Syntax)创建一个Vehicle实例,所谓的初始化语法就是写一个类型名字后面跟一个空的圆括号:
let someVehicle = Vehicle()
这样有了一个新的Vehicle实例,可以访问它的dscription属性来打印一个人类可读的当前交通工具的速度的描述:
println("Vehicle: \(someVehicle.description)") // Vehicle: traveling at 0.0 miles per hour
Vehicle类定义了随便一个交通工具的基本特性,但是对于它自身没有什么作用。想要让这些特性发挥作用,需要针对特定类型的交通工具做提高改进。
做出一个子类(Subclassing)
做出一个子类(subclassing)就是基于一个已经存在的类,创建一个新的类。子类继承了已经存在的类的特性,这些特性可以在子类中进行改进提高。同样的,你也可以给子类添加一些新的特性。
标识一个类拥有超类,需要在超类名字之前写上子类的名字,二者之间用冒号分割:
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实例都没有车筐。可以在创建完一个Bicycle实例之后设置它的hasBasket属性为true:
let bicycle = Bicycle() bicycle.hasBasket = true
同样可以修改一个Bicycle实例的继承属性currentSpeed,也可以查询实例的继承属性description:
bicycle.currentSpeed = 15.0 println("Bicycle: \(bicycle.description)") // Bicycle: traveling at 15.0 miles per hour
子类也可以被子类化(译者注:子类也可以被继承)。下面的例子创建了一个Bicycle类的子类,一个拥有两个座位的自行车:双座脚踏车(tandem):
class Tandem: Bicycle { var currentNumberOfPassengers = 0 }
Tandem继承了所有Bicycle的属性和方法,其中也包括了来自Vehicle的所有属性和方法。Tendem子类也添加了一个新的存储属性叫做currentNumberOfPassengers,这个属性的默认值是0.
创建了一个Tendem的实例之后,就可以处理它自身和继承来的属性,也可以查询来自Vehicle的只读属性description:
let tandem = Tandem() tandem.hasBasket = true tandem.currentNumberOfPassengers = 2 tandem.currentSpeed = 22.0 println("Tandem: \(tandem.description)") // Tandem: traveling at 22.0 miles per hour
重写(Overriding)
子类可以提供继承自超类的实例方法、类方法(译者注:这样也行?)、实例属性、类属性(译者注:这样也行?)和 下标的子类特有的实现。这被称作重写(overriding)。
为了重写一个继承来的特性,需要用override关键字做前缀定义。这样做明确表达了想要提供一个重写而不是由于失误提供了一个恰巧匹配的定义。意外的重写会导致不可预期的行为,没有override关键字的重写在代码编译的时候会被当成错误。
override关键字也会促使Swift编译器检查重写提供的定义是否和超类的相匹配。这个检查确保重写的定义是正确的。
访问超类方法、属性和下标
当给子类提供方法、属性和下标的重写时,有时需要用到已经存在的超类的实现作为子类重写的一部分。比如,可以提炼已经存在的行为,也可以存储继承来的变量的修改值(or store a modified value in an existing inherited variable)。
1:在一个重写方法someMethod实现中,可以用super.someMethod()调用超类的someMethod方法。
2:在一个重写的属性someProperty的getter和setter实现中,可以用super.someProperty()来调用超类的someProperty属性。
3:在一个对someIndex重写的下标实现中,可以使用super[someIndex]访问超类同样的下标。
重写方法(Overriding Methods)
在子类中可以重写继承来的实例方法或者类方法,来定制子类的实现。
下面的例子定义了一个Vehicle的新子类:Train,它重写了Train继承自Vehicle的makeNoise方法:
class Train: Vehicle { override func makeNoise() { println("Choo Choo") } }
如果创建一个Train实例,调用它的makeNoise方法,你会发现它的重写方法被调用了:
let train = Train() train.makeNoise() // prints "Choo Choo"
重写属性(Overriding Properties)
提供子类定制的getter和setter版本,可以重写超类的实例属性或类属性。也可以添加属性观察者来观察隐藏属性的修改。
重写属性的getter和setter
可以提供一个定制的getter(和setter,如果有必要的化)来重写任意继承的属性,不管原来的属性是存储属性还是计算属性。子类并不知道继承来的属性的是存储的还是计算的,子类只知道这些继承来的属性的名字和类型。
必须确保重载属性的名字和类型和超类中的一致,这样才会通过编译器的检查。
一个只读的超类属性在子类重写时可以同时提供getter和setter。但是不能将一个可读可写的属性重载为只读的。
NOTE
如果你重写了setter,你必须同时重写getter。如果不想在重写的getter中修改继承来的属性值,在getter中返回继承来的值通过Super.someProperty,这里的someProperty是重写的属性名称。
下面定义了一个新的类叫做Car,它是Vehicle类的子类。Car类有一个新的存储属性叫做gear(档位),grear有一个默认的整型1初始值。Car类重写了继承的description属性,提供了包括当前档位r的描述:
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 println("Car: \(car.description)") // Car: traveling at 25.0 miles per hour in gear 3
重写属性观察者
可以利用属性重写给继承来的属性添加观察者。这使得你可以在继承来的属性值发生变化后得到消息,不管那个属性的原始实现是怎么样的。更多的关于属性观察者的信息参考 Property Observers。
NOTE
重写不能给继承来的常量存储属性或者只读计算属性添加观察者。这些属性的值不能被设置,所以提供willSet和didSet是没有必要的。
同时重写setter和属性观察者也是不可以的。如果想观察属性值的变化,只需要在重写的setter中观察就可以了。 If you want to observe changes to a property’s value,and you are already providing a custom setter for that property,you can simply observe any value changes from within the custom setter.
下面例子定义了一个新的类叫做AutomaticCar,它是Car的子类。AutomaticCar表示有自动变速箱的汽车,会根据当前的速度选择合适的档位:
class AutomaticCar: Car { override var currentSpeed: Double { didSet { gear = Int(currentSpeed / 10.0) + 1 } } }
一旦设置了AutomaticCar实例中的currentSpeed属性,属性的didSet观察者会根据新速度设置一个合适的档位给实例的gear属性。特别的,属性观察者是这样选择档位的:将新的速度值除以10.0向下取整后加1.速度10会得到档位1,速度35.0得到4:
let automatic = AutomaticCar() automatic.currentSpeed = 35.0 println("AutomaticCar: \(automatic.description)") // AutomaticCar: traveling at 35.0 miles per hour in gear 4
阻止重写(Preventing Overrides)
做标记final可以给方法、属性、或者下标,用来防止它们被重写。具体需要在它们的前导关键字前加final(比如:final var,final func,final class func,final subscript)。
任何试图在子类中重写最终方法、属性或者下标的行为都会导致编译时错误。扩展定义中添加的方法、属性或者下标也可以添加final。
可以给整个类添加final,就是在class关键字前添加final标记(final class)。尝试将final类子类化会导致编译错误。