为什么必须有"继承"
在真实世界里,我们习惯把事物归类:车 → 自行车 → 双人自行车。
Swift 的 class 类型允许我们用同样的层级方式建模,把公共的代码放在"上层",把差异化的代码放在"下层",这就是继承(Inheritance)。
它带来的三大价值:
- 代码复用:公共逻辑写一次。
- 统一接口:上层可用"父类指针"操作一切子类。
- 多态:运行时才决定到底执行哪段代码。
基础概念速览
- 基类(Base Class):不继承任何类的类。
- 子类(Subclass):写在冒号后面的类,它自动拥有父类所有成员。
- 重写(Override):子类对继承来的成员重新实现,需加关键字 override。
- super:在子类内部访问"父类实现"的前缀。
- final:阻止后面的人再继续重写或继承。
基类长什么样
swift
/// 基类:最普通的"车"
class Vehicle {
var currentSpeed = 0.0 // 存储属性,默认 0
/// 计算属性:只读,返回人类可读的描述
var description: String {
return "traveling at \(currentSpeed) mph"
}
/// 实例方法:基类里什么都不做,留给子类去"填坑"
func makeNoise() {
// 空实现
}
}
// 使用
let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
// 打印:Vehicle: traveling at 0.0 mph
子类化(Subclassing)
语法:
swift
class 子类: 父类 { /* 新增或覆盖 */ }
示例 1:自行车
swift
class Bicycle: Vehicle {
var hasBasket = false // 新增属性
}
let bike = Bicycle()
bike.hasBasket = true
bike.currentSpeed = 15
print("Bicycle: \(bike.description)")
// 打印:Bicycle: traveling at 15.0 mph
示例 2:双人自行车(子类还能再被继承)
swift
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22
print("Tandem: \(tandem.description)")
// 打印:Tandem: traveling at 22.0 mph
重写(Override)全规则
- 方法重写:必须写 override;否则编译器报错。
swift
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
Train().makeNoise() // Choo Choo
-
属性重写
子类"看不到"父类属性到底是存储型还是计算型,只能按"名字 + 类型"去匹配。
a) 只读变读写:可以补充 setter。
b) 读写变只读:❌ 不允许。
示例:给车加"档位"描述
swift
class Car: Vehicle {
var gear = 1
override var description: String {
// 先拿父类描述,再拼接自己的
return super.description + " in gear \(gear)"
}
}
let car = Car()
car.currentSpeed = 25
car.gear = 3
print("Car: \(car.description)")
// 打印:Car: traveling at 25.0 mph in gear 3
-
属性观察器重写
可以为任何继承来的属性(无论存储/计算)添加 willSet/didSet。
典型场景:自动档根据速度换挡。
swift
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10) + 1 // 自己算档位
}
}
}
let auto = AutomaticCar()
auto.currentSpeed = 35
print("AutomaticCar: \(auto.description)")
// 打印:AutomaticCar: traveling at 35.0 mph in gear 4
防止继承与重写------final
- 防重写
swift
class Parent {
final var id = 1 // 子类不能 override
final func foo() {} // 子类不能 override
}
- 防继承
swift
final class Tool {} // 任何人写 class MyTool: Tool {} 都会编译失败
super 的使用场景小结
- 在 override 方法里:
super.someMethod() - 在 override 属性 getter/setter 里:
super.someProperty - 在 override 下标里:
super[index]
容易踩的坑
- 忘记写 override → 编译期报错。
- 重写时把只读属性改成只写 setter, Swift 不允许。
- 在 init 里访问 super 属性前,必须保证本类存储属性已初始化(初始化器规则)。
- 把 struct 拿去继承 → Swift 只有 class 支持继承,struct/enum 不行。
继承的边界与替代方案
继承是"白盒复用",子类会依赖父类实现细节,容易造成"脆弱基类"问题。Swift 提供了更轻量的组合手段:
- 协议(protocol)+ 默认实现 → 无需继承即可获得多态。
- 值类型(struct)+ 组合 → 把"能力"做成属性,而非父类。
- 面向协议编程(POP)→ 把"is-a"转成"can-do",降低耦合。
实战扩展:一个小型游戏对象模型
需求:
- 所有游戏实体都有血量 hp 与坐标 (x,y)。
- 玩家可以攻击,造成随机伤害。
- BOSS 血量低于 20% 时进入狂暴模式,攻击力翻倍。
代码:
swift
// 1. 基类
class Entity {
var hp: Double
var x: Double, y: Double
init(hp: Double, x: Double, y: Double) {
self.hp = hp; self.x = x; self.y = y
}
func attack() -> Double {
return 10.0 // 默认伤害
}
}
// 2. 普通小怪
class Monster: Entity {
override func attack() -> Double {
let damage = Double.random(in: 0..<6) + 5
return damage
}
}
// 3. BOSS
class Boss: Entity {
override func attack() -> Double {
let base = super.attack()
// 狂暴判断
let rage = hp < 20 // 假设 maxHp = 100
return rage ? base * 2 : base
}
}
// 4. 使用
let boss = Boss(hp: 15, x: 0, y: 0)
print("BOSS 伤害:\(boss.attack())") // 血量<20,伤害翻倍
总结与思考
- 继承是 Swift 面向对象体系的基石,但"能用"不等于"该用"。
- 优先把"共性"做成协议或工具函数,再考虑是否抽象出基类。
- 重写时始终加 override,既安全又自文档化。
- 用 final 明确"设计边界",让后来者少踩坑。
- 与值类型、协议、组合搭配,才能发挥 Swift 真正的威力。