Swift 中的继承(Inheritance)是面向对象编程的核心特性之一,仅类(class)支持继承,结构体和枚举不支持。继承允许子类复用、扩展或重写父类的属性、方法、下标等。
一、基本语法
class 父类 {
// 属性、方法、下标...
}
class 子类: 父类 {
// 可重写父类成员
}
二、核心示例:动物与狗
class Animal {
var name: String
var legs: Int
init(name: String, legs: Int) {
self.name = name
self.legs = legs
}
func speak() {
print("动物发出声音")
}
func description() -> String {
return "\(name) 有 \(legs) 条腿"
}
}
class Dog: Animal {
var breed: String
// 指定初始化器必须先初始化自身属性,再调用 super.init
init(name: String, breed: String) {
self.breed = breed
super.init(name: name, legs: 4)
}
// 重写方法
override func speak() {
print("汪汪!我是 \(name)")
}
// 重写属性(计算属性)
override var description: String {
return super.description + ",品种:\(breed)"
}
}
let dog = Dog(name: "旺财", breed: "拉布拉多")
dog.speak() // 汪汪!我是 旺财
print(dog.description()) // 旺财 有 4 条腿,品种:拉布拉多
三、继承的关键规则
| 规则 | 说明 |
|---|---|
| 仅类支持继承 | struct、enum 不能继承 |
| 单继承 | 一个类只能继承一个父类 |
| 初始化器继承 | 子类必须正确调用 super.init |
| 重写 | 用 override 标记 |
| 防止重写 | 用 final 修饰 |
四、初始化器继承规则(两阶段初始化)
1. 指定初始化器(Designated Initializer)
class Person {
var name: String
init(name: String) { self.name = name } // 指定
}
class Student: Person {
var studentID: String
init(name: String, studentID: String) {
self.studentID = studentID
super.init(name: name) // 必须调用父类指定初始化器
}
}
2. 便利初始化器(Convenience Initializer)
class Person {
var name: String
init(name: String) { self.name = name }
convenience init() {
self.init(name: "匿名")
}
}
class Student: Person {
var studentID: String
init(name: String, studentID: String) {
self.studentID = studentID
super.init(name: name)
}
// 便利初始化器必须调用本类的指定初始化器
convenience init(studentID: String) {
self.init(name: "学生", studentID: studentID)
}
}
五、重写(Override)
1. 重写方法
override func speak() { ... }
2. 重写属性(计算属性或观察器)
class Vehicle {
var speed: Double = 0
}
class Car: Vehicle {
override var speed: Double {
get { super.speed }
set { super.speed = min(newValue, 200) } // 限速
}
}
3. 重写下标
class Base {
subscript(index: Int) -> Int {
return index
}
}
class Derived: Base {
override subscript(index: Int) -> Int {
return index * 2
}
}
六、final 防止继承与重写
final class ImmutablePoint {
let x: Double, y: Double
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
// 不能被继承
class Shape {
final func area() -> Double { 0 }
}
// 不能被重写
七、类型转换(Type Casting)
1. is 检查类型
let animals: [Animal] = [Dog(name: "小黑", breed: "土狗"), Animal(name: "鸟", legs: 2)]
for animal in animals {
if animal is Dog {
print("\(animal.name) 是狗")
}
}
2. as? / as! 转换
for animal in animals {
if let dog = animal as? Dog {
print("狗狗品种:\(dog.breed)")
}
}
3. Any 和 AnyObject
let objects: [Any] = [1, "hello", Dog(name: "Tom", breed: "金毛")]
for obj in objects {
switch obj {
case let dog as Dog:
dog.speak()
case let str as String:
print("字符串: \(str)")
default:
break
}
}
八、继承 vs 组合(Composition)
| 继承 | 组合 |
|---|---|
| 是“is-a”关系 | 是“has-a”关系 |
| 强耦合 | 松耦合 |
| 易滥用 | 更灵活 |
推荐组合优于继承(Favor composition over inheritance)
// 组合:更好
protocol Engine {
func start()
}
class Car {
let engine: Engine
init(engine: Engine) { self.engine = engine }
}
// 继承:慎用
class ElectricCar: Car { ... }
九、继承链中的 deinit
class File {
deinit { print("文件关闭") }
}
class Document: File {
deinit { print("文档释放") }
}
var doc: Document? = Document()
doc = nil
// 文档释放
// 文件关闭
十、继承与协议结合(推荐)
protocol Flyable {
func fly()
}
class Bird: Animal, Flyable {
func fly() { print("\(name) 在飞") }
}
协议 + 继承 = 灵活设计
最佳实践总结
| 场景 | 推荐 |
|---|---|
| 共享代码 | 继承(谨慎) |
| 多态 | 继承 + override |
| 防止修改 | final |
| 灵活扩展 | 协议 + 组合 |
| 模型层 | struct + 协议(无继承) |
| UI 控制器 | class + 继承(如 UIViewController) |
常见面试题
- 结构体能继承吗?
→ 不能。仅class支持。 - 子类初始化器必须调用
super.init吗?
→ 是的(指定初始化器必须)。 - 如何防止某个方法被重写?
→final func override忘记写会怎样?
→ 编译错误(如果父类有同名方法)- 继承和协议哪个更推荐?
→ 协议优先,继承仅在必要时使用。
小技巧
// 1. 自动继承初始化器(当子类无新属性)
class A {
var x: Int
init(x: Int) { self.x = x }
}
class B: A {} // 自动继承 init(x:)
// 2. 使用 where 限制类型转换
for case let dog as Dog in animals where dog.breed == "金毛" {
print(dog.name)
}
// 3. 父类引用子类对象(多态)
let animal: Animal = Dog(name: "Jerry", breed: "柯基")
animal.speak() // 汪汪!我是 Jerry(动态派发)
高级话题(可继续提问)
- 方法派发机制(vtable、witness table)
- 类簇(Class Cluster)设计模式
- 继承与 ARC 内存管理
- SwiftUI 中
@Observable类与继承 - 协议导向编程(POP)替代继承
需要完整项目示例(如 MVC 继承结构)、性能分析,或与协议组合实战?欢迎继续提问!