15.3 重写

一个类继承另一个类的属性、方法、下标等特征后,子类可以重写(overriding)这些特征,overriding也有人翻译为“覆盖”,为了统一名称本书全部翻译为“重写”。下面我们就逐一介绍这些特征的重写。

15.3.1 属性重写

我们可以在子类中重写从父类继承来的属性,属性的重写一方面可以重写getter和setter访问器,另一方面可以重写属性观察者。

通过对第12章属性的学习,我们知道,计算类型属性需要使用getter和setter访问器,而存储属性不需要。子类在继承父类后,也可以通过getter和setter访问器重写父类的存储属性和计算属性。

下面看一个示例:

  1. class Person {
  2. var name : String
  3. var age : Int
  4. func description() -> String {
  5. return "\(name) 年龄是: \(age)"
  6. }
  7. init (name : String, age : Int) {
  8. self.name = name
  9. self.age = age
  10. }
  11. }
  12. class Student : Person {
  13. var school : String
  14. override var age : Int {
  15. get {
  16. return super.age
  17. }
  18. set {
  19. super.age = newValue < 8 ? 8 : newValue
  20. }
  21. }
  22. convenience init() {
  23. self.init(name : "Tony", age : 18, school : "清华大学")
  24. }
  25. init (name : String, age : Int, school : String) {
  26. self.school = school
  27. super.init(name : name, age : age)
  28. }
  29. }
  30. let student1 = Student()
  31. println("学生年龄:\(student1.age)")
  32. Student1.age = 6
  33. println("学生年龄:\(student1.age)")

上述代码第①行在Person类中定义存储name属性,第②行定义存储age属性。然后在Person的子类Student中重写age属性,其中第④~⑦行是重写代码,重写属性前面要添加override关键字,见代码第④行。在getter方法器中,第⑤行代码返回super.agesuper指代Person类实例,super.age是直接访问父类的age属性。在setter访问器中,第⑥行代码super.age = newValue < 8 ? 8 : newValue,是比较新值是否小于8岁(8岁为上学年龄),如果小于8岁,把8赋值给父类的age属性,否则把新值赋值给父类的age属性。

从属性重写可见,子类本身并不存储数据,数据是存储在父类的存储属性中的。

以上示例是重写属性getter和setter访问器,我们还可以重写属性观察者,代码如下:

  1. class Person {
  2. var name : String
  3. var age : Int
  4. func description() -> String {
  5. return "\(name) 年龄是: \(age)"
  6. }
  7. init (name : String, age : Int) {
  8. self.name = name
  9. self.age = age
  10. }
  11. }
  12. class Student : Person {
  13. var school : String
  14. override var age : Int {
  15. willSet {
  16. println("学生年龄新值:\(newValue)")
  17. }
  18. didSet{
  19. println("学生年龄旧值:\(oldValue)")
  20. }
  21. }
  22. convenience init() {
  23. self.init(name : "Tony", age : 18, school : "清华大学")
  24. }
  25. init (name : String, age : Int, school : String) {
  26. self.school = school
  27. super.init(name : name, age : age)
  28. }
  29. }
  30. let student1 = Student()
  31. println("学生年龄:\(student1.age)")
  32. Student1.age = 6
  33. println("学生年龄:\(student1.age)")

上述代码第①~⑥行重写了age属性观察者。重写属性前面要添加override关键字,见代码第①行。如果只关注修改之前的调用,可以只重写willSet观察者;如果只关注修改之后的调用,可以只重写didSet观察者,总之是比较灵活的。在观察者中,还可以使用系统分配默认参数newValueoldValue

代码第⑦行修改了age属性,修改前后的输出结果如下:

  1. 学生年龄新值:6
  2. 学生年龄旧值:18

提示 一个属性重写了观察者后,就不能同时对getter和setter访问器重写。另外,常量属性和只读计算属性也都不能重写属性观察者。

15.3.2 方法重写

我们可以在子类中重写从父类继承来的实例方法和静态方法(又称为类方法)。

下面看一个示例:

  1. class Person {
  2. var name : String
  3. var age : Int
  4. func description() -> String {
  5. return "\(name) 年龄是: \(age)"
  6. }
  7. class func printlnClass() ->() {
  8. println( "Person 打印...")
  9. }
  10. init (name : String, age : Int) {
  11. self.name = name
  12. self.age = age
  13. }
  14. }
  15. class Student : Person {
  16. var school : String
  17. convenience init() {
  18. self.init(name : "Tony", age : 18, school : "清华大学")
  19. }
  20. init (name : String, age : Int, school : String) {
  21. self.school = school
  22. super.init(name : name, age : age)
  23. }
  24. override func description() -> String {
  25. println("父类打印 \(super.description())")
  26. return "\(name) 年龄是: \(age), 所在学校: \(school)。"
  27. }
  28. override class func printlnClass() ->() {
  29. println( "Student 打印...")
  30. }
  31. }
  32. let student1 = student()
  33. println("学生1:\(student1.description())")
  34. Person.printlnClass()
  35. Student.printlnClass()

Person类中,第①行代码是定义实例方法description,第②行代码是定义静态方法printlnClass,然后在Person类的子类Student类中重写descriptionprintlnClass方法,代码第③行是重写实例方法description,重写的方法前面要添加关键字override。第④行代码使用super.description()语句调用父类的description方法,其中super指代父类实例。

第⑤行代码是重写静态方法printlnClass,在静态方法中不能访问实例属性。

最后第⑥行调用了description方法。由于在子类中重写了该方法,所以调用的是子类中的description方法。输出结果是:

  1. 父类打印 Tony 年龄是: 18
  2. 学生1Tony 年龄是: 18, 所在学校: 清华大学。

为了测试静态方法重写,第⑦行调用了Person.printlnClass()语言,它是调用父类的printlnClass静态方法,输出结果是:

  1. Person 打印...

第⑧行调用了Student.printlnClass()语言,它是调用子类的printlnClass静态方法,输出结果是:

  1. Student 打印...

15.3.3 下标重写

下标是一种特殊属性。子类属性重写是重写属性的getter和setter访问器,对下标的重写也是重写下标的getter和setter访问器。

下面看一个示例:

  1. class DoubleDimensionalArray {
  2. let rows: Int, columns: Int
  3. var grid: [Int]
  4. init(rows: Int, columns: Int) {
  5. self.rows = rows
  6. self.columns = columns
  7. grid = Array(count: rows * columns, repeatedValue: 0)
  8. }
  9. subscript(row: Int, col: Int) -> Int {
  10. get {
  11. return grid[(row columns) + col]
  12. }
  13. set {
  14. grid[(row columns) + col] = newValue
  15. }
  16. }
  17. }
  18. class SquareMatrix : DoubleDimensionalArray {
  19. override subscript(row: Int, col: Int) -> Int {
  20. get {
  21. return super.grid[(row columns) + col]
  22. }
  23. set {
  24. super.grid[(row columns) + col] = newValue * newValue
  25. }
  26. }
  27. }
  28. var ary2 = SquareMatrix(rows: 5, columns: 5)
  29. for var i = 0; i < 5; i++ {
  30. for var j = 0; j < 5; j++ {
  31. ary2[i,j] = i + j
  32. }
  33. }
  34. for var i = 0; i < 5; i++ {
  35. for var j = 0; j < 5; j++ {
  36. print("\t\t \(ary2[i,j])")
  37. }
  38. print("\n")
  39. }

上述代码第①行定义了类DoubleDimensionalArray,它在代码第②行和第③行定义了下标。第④行代码定义了类SquareMatrix,它继承了DoubleDimensionalArray类,并且在第④~⑩行重写了父类的下标。与其他类的重写类似,前面需要添加关键字override,见代码第⑤行。第⑥行是重写getter访问器,其中的第⑦行super.grid[(row * columns) + col]语句中使用super调用父类的grid属性。第⑧行代码是重写setter访问器,其中的第⑨行super.grid[(row columns) + col] = newValue newValue语句是给父类的grid属性赋值。

15.3.4 使用final关键字

我们可以在类的定义中使用final关键字声明类、属性、方法和下标。final声明的类不能被继承,final声明的属性、方法和下标不能被重写。

下面看一个示例:

  1. final class Person {
  2. var name : String
  3. final var age : Int
  4. final func description() -> String {
  5. return "\(name) 年龄是: \(age)"
  6. }
  7. final class func printlnClass() ->() {
  8. println( "Person 打印...")
  9. }
  10. init (name : String, age : Int) {
  11. self.name = name
  12. self.age = age
  13. }
  14. }
  15. class Student : Person { //编译错误 ⑤
  16. var school : String
  17. convenience init() {
  18. self.init(name : "Tony", age : 18, school : "清华大学")
  19. }
  20. init (name : String, age : Int, school : String) {
  21. self.school = school
  22. super.init(name : name, age : age)
  23. }
  24. override func description() -> String { //编译错误 ⑥
  25. println("父类打印 \(super.description())")
  26. return "\(name) 年龄是: \(age), 所在学校: \(school)。"
  27. }
  28. override class func printlnClass() ->() { //编译错误 ⑦
  29. println( "Student 打印...")
  30. }
  31. override var age : Int { //编译错误 ⑧
  32. get {
  33. return super.age
  34. }
  35. set {
  36. super.age = newValue < 8 ? 8 : newValue
  37. }
  38. }
  39. }

上述代码第①行定义Person类,它被声明为final,说明它是不能被继承的,因此代码第⑤行定义Student类,并声明为Person子类时,会报如下编译错误:

  1. Inheritance from a final class 'Person'

第②行定义的age属性也是final,那么在代码第⑧行试图重写age属性时,会报如下编译错误:

  1. Var overrides a 'final' var

第③行定义description实例方法,它被声明为final,那么在代码第⑥行试图重写description实例方法时,会报如下编译错误:

  1. Instance method overrides a 'final' instance method

第④行定义printlnClass静态方法,它被声明为final,那么在代码第⑦行试图重写printlnClass静态方法时,会报如下编译错误:

  1. Class method overrides a 'final' class method

使用final可以控制我们的类被有限地继承,特别是在开发一些商业软件时,适当地添加final限制是非常有必要的。