首页 / Swift 编程语言教程 / 继承

Swift 编程语言教程

继承

本教程共 93 篇 · 第 61 篇 · 更新于 2026-08-08 · 约 8 分钟阅读

SwiftSwift 编程语言教程继承基类子类superfinal

本节目标:学完你能用冒号让一个类继承另一个类,复用其成员,并用 super 调用父类版本的实现。

继承(inheritance)是类区别于结构体、枚举的根本能力。它允许一个类把另一个类的属性、方法、下标接过来用,还能在此基础上加新东西或改旧行为。通过继承,你能把通用逻辑放在父类,把特化逻辑放在子类,避免重复代码。这一章先把继承的骨架搭起来,重写细节留到第 62 章。

1-1 什么是基类

不被任何其他类继承的类,叫基类(base class)。Swift 有个特别之处:类不需要从一个统一的”总父类”继承。你直接写 class Foo {},它就是一个干净的基类,没有隐藏的祖宗。

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "以 \(currentSpeed) 英里每小时行驶"
    }
    func makeNoise() {
        // 基类啥也不做,留给孩子去实现
    }
}

Vehicle 是个基类,它描述了”车”的共性:currentSpeed 速度、description 描述、以及一个空的 makeNoise() 发声方法。基类自己用处有限,但它是所有具体车型的地基。

1-2 用冒号写子类

让一个类继承另一个类,写法是在子类名后面加冒号,再写父类名:

class Bicycle: Vehicle {
    var hasBasket = false
}

Bicycle 继承了 Vehicle,自动获得了 currentSpeeddescriptionmakeNoise() 这些成员,自己只需加一个 hasBasket 属性。创建实例后,继承来的东西直接用:

let bicycle = Bicycle()
bicycle.currentSpeed = 15.0
print(bicycle.description)   // 以 15.0 英里每小时行驶

子类还能再被继承,形成链条。比如双人自行车 Tandem 继承 Bicycle,而 Bicycle 继承 Vehicle,于是 Tandem 一路继承了全部三代的成员。继承链越长,最底层的类能用的能力越丰富。

1-3 重写是怎么发生的

子类可以提供自己的版本,去替换从父类继承来的方法、属性或下标,这叫重写(override)。比如 Train 继承 Vehicle 后,给 makeNoise() 一个具体实现:

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

这里 override 关键字是强制的。它有两个作用:一是明说”我就是要重写,不是不小心写了个同名方法”;二是让编译器去父类核对,确认父类真有这个可被重写的成员,防止你拼错名字或类型对不上。漏写 override 直接编译报错,这层保护能拦掉很多低级 bug。重写的完整规则我们在第 62 章展开。

1-4 用 super 调用父类版本

重写时,有时你不想完全丢掉父类逻辑,而是想”在父类基础上再加点料”。这时用 super 前缀去调父类的版本:

  • 重写了方法,在方法体里写 super.someMethod() 调用父类同名方法;
  • 重写了属性,在 getter/setter 里写 super.someProperty 拿到父类的值;
  • 重写了下标,写 super[someIndex] 访问父类版本。
class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + ",挡位 \(gear)"
    }
}

Car 重写了 description,但它没有把父类的描述推倒重来,而是先 super.description 拿到”以 x 英里每小时行驶”,再拼上自己的挡位信息。这样父类的心血被复用,子类只补差异。这种”调用父类再加工”的模式在继承里极常见。

1-5 阻止继承和重写:final

不是所有东西都该被改。如果你希望某个方法、属性、下标,或者整个类,不能被子类动,就用 final 修饰。

class LockedBase {
    final var fixedID: Int = 0
    final func lockedMethod() {}
}

final class Unbreakable {
    // 整个类都被封死,不能当父类
}

final 写在关键字前面,比如 final varfinal funcfinal class funcfinal subscript。任何子类试图重写被 final 的成员,都是编译错误。final class 则连被继承本身都不允许。注意 final 只能用在类成员上,顶层声明不能加 final

什么时候用 final?当你确定某个行为就是最终版本、子类不该改它,或者这个类设计上就不该被扩展(比如为了性能或安全),就果断 final。它既是一种设计意图的声明,也是一道编译期的护栏。

1-6 继承不是白拿的

继承带来复用,也带来复杂度。每多一层继承,子类就多了一份对父类的依赖:父类一改,所有子类都可能受影响。所以 Swift 官方一直强调”优先用结构体”,继承只在确实需要”is-a”关系、且要复用或特化行为时才上。

另外记住:只有类能继承。结构体和枚举再想要”复用另一套类型的逻辑”,也只能走组合(把一个类型作为属性装进来)或协议(第 67 章),走不了继承。理解这条边界,你就不会在错误的类型上硬套继承。

1-7 小结

继承让你用 子类名: 父类名 复用并扩展父类成员;override 是重写的强制标记,漏写或写错都会编译失败;super 让你在重写时复用父类原版;final 则把成员或整个类锁死。把这套骨架记牢,第 62 章我们钻进重写的各种具体姿势。

1-8 继承链上的初始化约定

子类继承父类后,初始化责任也跟着传下来。子类必须保证:自己引入的属性有初值,并且调用父类初始化器让父类部分也就绪。这正是第 64 章二段式初始化的由来。继承越多层,这条”自底向上填属性、再自顶向下加工”的链路越长。新手在继承里最常踩的坑,就是忘了调 super.init(),或者调的时机不对,这些都会被编译器的安全检查拦下。

1-9 组合优先于继承

面向对象刚流行时,很多人什么都用继承,结果继承树又深又脆。Swift 社区更推崇”组合优于继承”:与其让 Car 继承 Vehicle 再加一堆东西,不如把”引擎""轮子”作为属性组合进 Car。组合更灵活、耦合更低,也不会被单一父类锁死。继承只在该用”is-a”关系、且要复用或特化行为时才合适。作为初学者,先想”这个类是不是另一种父类”,想清楚再决定继承,多数时候组合是更好的答案。苹果的很多 API 也在往组合与协议方向走,而不是深继承。把继承当作少数派工具,你的设计会更健壮。另外,过深的继承会让子类过度依赖父类实现细节,父类一改,子类可能莫名其妙出错。保持继承链浅、关系清晰,是写可维护 Swift 的基本功。

1-10 继承不是越多越好

工程经验告诉我们,继承层次越深,理解和维护成本越高。每多一层,子类就对父类多一份隐式依赖,父类一改,底下全受影响。所以 Swift 官方甚至不提供”总父类”,就是不想让你无脑往上挂。真正合适的继承通常只有一两层:基类放共性,子类放特化。如果发现自己想建三层以上的继承,先停下来想想能不能用组合或协议替代。浅而清晰的继承,远比深而精巧的继承好维护。