diff --git a/source/chapter2/13_Inheritance.md b/source/chapter2/13_Inheritance.md index 460918b8..7a5d75d0 100755 --- a/source/chapter2/13_Inheritance.md +++ b/source/chapter2/13_Inheritance.md @@ -25,44 +25,34 @@ > 注意: Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。 -下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了两个对所有车辆都通用的属性(`numberOfWheels`和`maxPassengers`)。这些属性在`description`方法中使用,这个方法返回一个`String`类型的,对车辆特征的描述: +下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算属性`description`使用,用来创建车辆的描述。 + +`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制 ```swift -class Vehicle { - var numberOfWheels: Int - var maxPassengers: Int - func description() -> String { - return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" - } - init() { - numberOfWheels = 0 - maxPassengers = 1 - } +class Vehicle { + var currentSpeed = 0.0 + var description: String { + return "traveling at \(currentSpeed) miles per hour" + } + func makeNoise() { + // 什么也不做-因为车辆不一定会有噪音 + } } ``` - -`Vehicle`类定义了*构造器(initializer)*来设置属性的值。构造器会在[构造过程](../chapter2/_14Initialization.html)一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。 - -构造器用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。 - -构造器的最简单形式就像一个没有参数的实例方法,使用`init`关键字: - -```swift -init() { - // 执行构造过程 -} + +您可以用初始化语法创建一个`Vehicle `的新实例,即 `TypeName`后面跟一个空括号: +```swift +let someVehicle = Vehicle() +``` + +现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。 + +```swift +println("Vehicle: \(someVehicle.description)") +// Vehicle: traveling at 0.0 miles per hour ``` -如果要创建一个`Vehicle`类的新实例,使用*构造器*语法调用上面的初始化器,即类名后面跟一个空的小括号: - -```swift -let someVehicle = Vehicle() -``` - -这个`Vehicle`类的构造器为任意的一辆车设置一些初始化属性值(`numberOfWheels = 0 `和`maxPassengers = 1`)。 - -`Vehicle`类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。 - ## 子类生成(Subclassing) @@ -83,56 +73,50 @@ class SomeClass: SomeSuperclass { “定义一个新的类叫`Bicycle `,它继承了`Vehicle`的特性”; ```swift -class Bicycle: Vehicle { - init() { - super.init() - numberOfWheels = 2 - } -} -``` -preview - `Bicycle`是`Vehicle`的子类,`Vehicle`是`Bicycle`的超类。新的`Bicycle`类自动获得`Vehicle`类的特性,比如 `maxPassengers`和`numberOfWheels`属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述`Bicycle`类。 - -`Bicycle`类定义了一个构造器来设置它定制的特性(自行车只有2个轮子)。`Bicycle`的构造器调用了它父类`Vehicle`的构造器 `super.init()`,以此确保在`Bicycle`类试图修改那些继承来的属性前`Vehicle`类已经初始化过它们了。 - -> 注意: -不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见[初始化器的继承与重写](../chapter2/_14Initialization.html#initializer_inheritance_and_ overriding) - -`Vehicle`类中`maxPassengers`的默认值对自行车来说已经是正确的,因此在`Bicycle`的构造器中并没有改变它。而`numberOfWheels`原来的值对自行车来说是不正确的,因此在初始化器中将它更改为 2。 - -`Bicycle`不仅可以继承`Vehicle`的属性,还可以继承它的方法。如果你创建了一个`Bicycle`类的实例,你就可以调用它继承来的`description`方法,并且可以看到,它输出的属性值已经发生了变化: - -```swift -let bicycle = Bicycle() -println("Bicycle: \(bicycle.description())") -// Bicycle: 2 wheels; up to 1 passengers -``` - -子类还可以继续被其它类继承: - -```swift -class Tandem: Bicycle { - init() { - super.init() - maxPassengers = 2 - } +class Bicycle: Vehicle { + var hasBasket = false } ``` -上面的例子创建了`Bicycle`的一个子类:双人自行车(tandem)。`Tandem`从`Bicycle`继承了两个属性,而这两个属性是`Bicycle`从`Vehicle`继承而来的。`Tandem`并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改`maxPassengers`的值,因为双人自行车可以坐两个人。 - -> 注意: -子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。 - -创建一个`Tandem`类的实例,打印它的描述,即可看到它的属性已被更新: - -```swift -let tandem = Tandem() -println("Tandem: \(tandem.description())") -// Tandem: 2 wheels; up to 2 passengers +新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed `和`description`属性,还有它的`makeNoise`方法。 + +除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储属性`hasBasket`(属性推断为`Bool`)。 + +默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`: + +```swift +let bicycle = Bicycle() +bicycle.hasBasket = true +``` + +你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性: + +```swift +bicycle.currentSpeed = 15.0 +println("Bicycle: \(bicycle.description)") +// Bicycle: traveling at 15.0 miles per hour ``` -注意,`Tandem`类也继承了`description`方法。一个类的实例方法会被这个类的所有子类继承。 +子类还可以继续被其它类继承,下面的示例为`Bicycle `创建了一个名为`Tandem `(双人自行车)的子类: + +```swift +class Tandem: Bicycle { + var currentNumberOfPassengers = 0 +} +``` + +`Tandem`从`Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储属性,默认值为0。 + +如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description `: + +```swift +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) @@ -157,35 +141,22 @@ println("Tandem: \(tandem.description())") 在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。 -下面的例子定义了`Vehicle`的一个新的子类,叫`Car`,它重写了从`Vehicle`类继承来的`description`方法: +下面的例子定义了`Vehicle`的一个新的子类,叫`Train `,它重写了从`Vehicle`类继承来的`makeNoise `方法: ```swift -class Car: Vehicle { - var speed: Double = 0.0 - init() { - super.init() - maxPassengers = 5 - numberOfWheels = 4 - } - override func description() -> String { - return super.description() + "; " - + "traveling at \(speed) mph" - } +class Train: Vehicle { + override func makeNoise() { + println("Choo Choo") + } } ``` -`Car`声明了一个新的存储型属性`speed`,它是`Double`类型的,默认值是`0.0`,表示“时速是0英里”。`Car`有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。 - -`Car`重写了继承来的`description`方法,它的声明与`Vehicle`中的`description`方法一致,声明前面加上了`override`关键字。 - -`Car`中的`description`方法并非完全自定义,而是通过`super.description`使用了超类`Vehicle`中的`description`方法,然后再追加一些额外的信息,比如汽车的当前速度。 - -如果你创建一个`Car`的新实例,并打印`description`方法的输出,你就会发现描述信息已经发生了改变: +如果你创建一个`Train `的新实例,并调用了它的`makeNoise `方法,你就会发现`Train `版本的方法被调用 ```swift -let car = Car() -println("Car: \(car.description())") -// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph +let train = Train() +train.makeNoise() +// prints "Choo Choo" ``` ### 重写属性 @@ -201,30 +172,27 @@ println("Car: \(car.description())") > 注意: 如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回`super.someProperty`来返回继承来的值。正如下面的`SpeedLimitedCar`的例子所示。 -以下的例子定义了一个新类,叫`SpeedLimitedCar`,它是`Car`的子类。类`SpeedLimitedCar`表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的`speed`属性来实现这个速度限制: +以下的例子定义了一个新类,叫`Car`,它是`Vehicle `的子类。这个类引入了一个新的存储属性叫做`gear `,默认为整数1。`Car`类重写了继承自`Vehicle `的description属性,提供自定义的,包含当前档位的描述: ```swift -class SpeedLimitedCar: Car { - override var speed: Double { - get { - return super.speed - } - set { - super.speed = min(newValue, 40.0) - } - } +class Car: Vehicle { + var gear = 1 + override var description: String { + return super.description + " in gear \(gear)" + } } -``` - -当你设置一个`SpeedLimitedCar`实例的`speed`属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的`speed`设置为`newValue`和`40.0`中较小的那个。这两个值哪个较小由`min`函数决定,它是Swift标准库中的一个全局函数。`min`函数接收两个或更多的数,返回其中最小的那个。 - -如果你尝试将`SpeedLimitedCar`实例的`speed`属性设置为一个大于40mph的数,然后打印`description`函数的输出,你会发现速度被限制在40mph: +``` + +重写的`description `属性,首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car `类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。 + +如果你创建了`Car `的实例并且设置了它的`gear`和`currentSpeed`属性,你可以看到它的`description`返回了`Car`中定义的`description`: ```swift -let limitedCar = SpeedLimitedCar() -limitedCar.speed = 60.0 -println("SpeedLimitedCar: \(limitedCar.description())") -// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph +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 Observer) @@ -234,37 +202,33 @@ println("SpeedLimitedCar: \(limitedCar.description())") > 注意: 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet`或`didSet`实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。 -下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。`AutomaticCar`也提供了定制的`description`方法,可以输出当前挡位。 +下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位: ```swift -class AutomaticCar: Car { - var gear = 1 - override var speed: Double { - didSet { - gear = Int(speed / 10.0) + 1 - } - } - override func description() -> String { - return super.description() + " in gear \(gear)" - } +class AutomaticCar: Car { + override var currentSpeed: Double { + didSet { + gear = Int(currentSpeed / 10.0) + 1 + } + } } ``` -当你设置`AutomaticCar`的`speed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4: +当你设置`AutomaticCar`的`currentSpeed `属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4: ```swift -let automatic = AutomaticCar() -automatic.speed = 35.0 -println("AutomaticCar: \(automatic.description())") -// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4 +let automatic = AutomaticCar() +automatic.currentSpeed = 35.0 +println("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`特性即可。(例如:`final var`, `final func`, `final class func`, 以及 `final subscript`) 如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。 -你可以通过在关键字`class`前添加`@final`特性(`@final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。 +你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。