Swift: Swift 继承与多态:面向对象编程核心概念
继承是面向对象编程的三大特性之一,允许一个类基于另一个类构建,复用和扩展已有行为。本课深入 Swift 的继承机制与多态的应用。
1. 你将学到
- 使用继承创建子类
override关键字重写方法、属性和下标super关键字调用父类实现final关键字阻止继承和重写- 向上转型(upcasting)与多态的运行机制
2. 一个 UI 开发者的真实故事
(1) 痛点:UI 组件大量重复代码
Charlie 开发 UI 组件库,需要 Button、Label、TextField 等组件,它们都有相似的基础行为:
SWIFT
class Button {
var frame: CGRect
var backgroundColor: UIColor
func render() { /* 绘制背景 */ }
func handleTap() { /* 处理点击 */ }
}
class Label {
var frame: CGRect
var backgroundColor: UIColor
func render() { /* 绘制背景 */ }
}
Button 和 Label 中的 frame、backgroundColor、render() 几乎一样。每次新增组件都复制粘贴相同的属性,修改基础行为要改 10 个文件。
(2) 继承的解法
SWIFT
class UIView {
var frame: CGRect
var backgroundColor: UIColor
func render() { /* 绘制基础背景 */ }
}
class Button: UIView {
override func render() {
super.render() // 先画背景
// 再画按钮特有的样式
}
func handleTap() { }
}
class Label: UIView {
override func render() {
super.render()
// 再画标签特有的样式
}
}
公共属性定义一次,所有子类复用。修改基础行为只需改一个文件。
(3) 收益:复用 + 可扩展
| 维度 | 复制粘贴式 | 继承式 |
|---|---|---|
| 代码复用 | 0%——每个组件独立 | 100%——基础属性和方法 |
| 修改成本 | 改 10 个文件 | 改 1 个父类 |
| 新增组件 | 复制整个文件 | 继承 + 添加差异 |
| 一致性 | 容易遗漏修改 | 父类保证行为一致 |
| 可扩展性 | 重写风险高 | override + super 可控 |
3. 继承的基础
(1) 子类化
graph TB
A[Animal] --> B["属性: name, age"]
A --> C["方法: makeSound()"]
B --> D[Dog]
C --> D
D --> E["属性: breed"]
D --> F["重写: makeSound()"]
A --> G[Cat]
G --> H["属性: color"]
G --> I["重写: makeSound()"]
| 概念 | 说明 |
|---|---|
| 基类/父类 | 被继承的类,Swift 中所有类都继承自 AnyObject |
| 子类 | 继承父类的类,可以添加新属性和方法 |
| 单继承 | Swift 只支持单继承(一个子类只有一个父类) |
| 根类 | 没有父类的类称为根类 |
▶ 示例:基本继承
SWIFT
// ============================================
// 继承的基本用法
// ============================================
// 基类
class Vehicle {
var make: String
var model: String
var year: Int
init(make: String, model: String, year: Int) {
self.make = make
self.model = model
self.year = year
}
func description() -> String {
return "\(year) \(make) \(model)"
}
func startEngine() {
print("Engine started")
}
}
// 子类——继承 Vehicle
class Car: Vehicle {
var numberOfDoors: Int
init(make: String, model: String, year: Int, numberOfDoors: Int) {
self.numberOfDoors = numberOfDoors
super.init(make: make, model: model, year: year) // 调用父类初始化器
}
func honk() {
print("Beep beep!")
}
}
// 子类——继承 Vehicle
class Motorcycle: Vehicle {
var hasSidecar: Bool
init(make: String, model: String, year: Int, hasSidecar: Bool = false) {
self.hasSidecar = hasSidecar
super.init(make: make, model: model, year: year)
}
func wheelie() {
print("Doing a wheelie!")
}
}
let car = Car(make: "Toyota", model: "Camry", year: 2022, numberOfDoors: 4)
print(car.description()) // 继承的方法
car.honk() // Car 自己的方法
输出:
TEXT 📖 仅展示2022 Toyota Camry Beep beep!
4. 重写与 super
(1) override 重写
子类可以用 override 重写父类的方法、计算属性或下标:
| 可重写的内容 | override 要求 |
说明 |
|---|---|---|
| 实例方法 | ✅ 必须加 | 提供新的函数体 |
| 计算属性 | ✅ 必须加 | 提供新的 getter/setter |
| 下标 | ✅ 必须加 | 提供新的下标实现 |
| 存储属性 | ❌ 不能 | 存储属性不能重写 |
▶ 示例:重写方法
SWIFT
// ============================================
// override 重写父类方法
// ============================================
class Animal {
var name: String
init(name: String) { self.name = name }
func makeSound() {
print("\(name) makes a sound")
}
}
class Dog: Animal {
override func makeSound() {
print("\(name) barks: Woof!")
}
}
class Cat: Animal {
override func makeSound() {
print("\(name) meows: Meow!")
}
}
let dog = Dog(name: "Buddy")
let cat = Cat(name: "Whiskers")
dog.makeSound()
cat.makeSound()
输出:
TEXT 📖 仅展示Buddy barks: Woof! Whiskers meows: Meow!
(2) super 调用父类
在重写中通过 super 调用父类版本:
SWIFT
// ============================================
// super 关键字调用父类
// ============================================
class Shape {
var color: String
init(color: String) {
self.color = color
print("Shape init: \(color)")
}
func draw() {
print("Drawing a shape in \(color)")
}
}
class Circle: Shape {
var radius: Double
init(color: String, radius: Double) {
self.radius = radius
super.init(color: color) // 先完成自己的初始化,再调父类
}
override func draw() {
super.draw() // 先执行父类的绘制
print(" It's a circle with radius \(radius)")
}
}
let circle = Circle(color: "Red", radius: 5.0)
circle.draw()
输出:
TEXT 📖 仅展示Shape init: Red Drawing a shape in Red It's a circle with radius 5💡 提示: 初始化时,子类必须先完成自己的存储属性赋值,再调用
super.init。这是 Swift 的两阶段初始化安全机制。
5. final 与多态
(1) final 阻止继承
final 关键字可以阻止类被继承,或阻止方法/属性被重写:
| 用法 | 效果 |
|---|---|
final class |
该类不能被继承 |
final func |
该方法不能被重写 |
final var |
该属性不能被重写 |
▶ 示例:final 的使用
SWIFT
// ============================================
// final 阻止继承和重写
// ============================================
class Calculator {
// 基础运算可重写
func add(_ a: Int, _ b: Int) -> Int { return a + b }
// 核心逻辑不允许重写
final func process(_ a: Int, _ b: Int, operation: String) -> String {
let result: Int
switch operation {
case "add": result = add(a, b)
default: result = 0
}
return "Result: \(result)"
}
}
// 这个子类可以 override add,但不能 override process
class AdvancedCalculator: Calculator {
override func add(_ a: Int, _ b: Int) -> Int {
print("Advanced add: \(a) + \(b)")
return a + b
}
// override func process(...) // 编译错误!
}
let calc = AdvancedCalculator()
print(calc.process(3, 4, operation: "add"))
输出:
TEXT 📖 仅展示Advanced add: 3 + 4 Result: 7
(2) 多态
多态允许用父类类型的变量引用子类实例,调用时执行子类的重写实现:
graph TB
A["let shapes: [Shape] = [Circle(), Rectangle()]"] --> B["for shape in shapes"]
B --> C["shape.draw() ← 多态"]
C --> D["Circle 的 draw()"]
C --> E["Rectangle 的 draw()"]
▶ 示例:多态的实际应用
SWIFT
// ============================================
// 多态——统一处理不同类型的对象
// ============================================
class Employee {
let name: String
init(name: String) { self.name = name }
func work() -> String {
return "\(name) is working"
}
func bonus() -> Double {
return 0
}
}
class Developer: Employee {
override func work() -> String { return "\(name) is writing code" }
override func bonus() -> Double { return 5000 }
}
class Designer: Employee {
override func work() -> String { return "\(name) is designing UI" }
override func bonus() -> Double { return 3000 }
}
class Manager: Employee {
override func work() -> String { return "\(name) is managing team" }
override func bonus() -> Double { return 8000 }
}
// 多态:统一处理不同子类
let team: [Employee] = [
Developer(name: "Alice"),
Designer(name: "Bob"),
Manager(name: "Charlie")
]
var totalBonus = 0.0
for member in team {
print(member.work()) // 动态调用各自的实现
totalBonus += member.bonus()
}
print("Total bonus: $\(totalBonus)")
输出:
TEXT 📖 仅展示Alice is writing code Bob is designing UI Charlie is managing team Total bonus: $16000.0
6. 完整示例:UI 组件渲染系统
SWIFT
// ============================================
// 完整示例:UI 组件渲染系统
// 功能:继承 + 重写 + super + final + 多态综合
// ============================================
import Foundation
// 1. 基类(可继承)
class UIComponent {
let id: String
var x: Int
var y: Int
init(id: String, x: Int, y: Int) {
self.id = id
self.x = x
self.y = y
}
func render() -> String {
return "[\(id)] at (\(x), \(y))"
}
// 核心逻辑——禁止重写
final func display() {
print(render())
}
}
// 2. 按钮子类
class Button: UIComponent {
let label: String
init(id: String, x: Int, y: Int, label: String) {
self.label = label
super.init(id: id, x: x, y: y)
}
override func render() -> String {
let base = super.render()
return "\(base) [Button: \(label)]"
}
}
// 3. 文本框子类
class TextField: UIComponent {
var text: String
init(id: String, x: Int, y: Int, text: String = "") {
self.text = text
super.init(id: id, x: x, y: y)
}
override func render() -> String {
return "\(super.render()) [TextField: \"\(text)\"]"
}
}
// 4. 多态渲染
let components: [UIComponent] = [
Button(id: "btn1", x: 10, y: 20, label: "Submit"),
TextField(id: "txt1", x: 10, y: 60, text: "Hello"),
Button(id: "btn2", x: 10, y: 100, label: "Cancel"),
TextField(id: "txt2", x: 10, y: 140)
]
for component in components {
component.display() // 多态调用
}
输出:
TEXT 📖 仅展示[btn1] at (10, 20) [Button: Submit] [txt1] at (10, 60) [TextField: "Hello"] [btn2] at (10, 100) [Button: Cancel] [txt2] at (10, 140) [TextField: ""]
❓ 常见问题
Q Swift 支持多继承吗?
A 不支持。Swift 是单继承语言(一个子类只能有一个父类)。多继承的需求通过协议(Protocol)实现,一个类可以遵守多个协议。
Q 为什么有时重写方法时必须调 super?
A 不是必须的,但如果不调
super,父类中的逻辑就完全被替换了。通常做法是在子类中先调 super 执行公共逻辑,再补充子类特有的行为。是否调 super 取决于设计意图。Q final class 和 struct 在不可继承上一样吗?
A 表现上一样——都不能被继承。但 struct 是值类型,class 是引用类型。用
final class 在保持引用类型特性的同时阻止继承。如果既不需要继承也不需要引用语义,就用 struct。Q 向上转型(upcasting)和向下转型有什么区别?
A 向上转型是将子类对象当作父类类型使用(自动隐式,如
Employee e = Developer(...))。向下转型是将父类引用转回子类类型(需用 as? 或 as!),转换可能失败所以必须是可选类型。Q 初始化时为什么必须先初始化自己的存储属性再调 super.init?
A 这是 Swift 两阶段初始化安全机制。阶段 1:从子类到父类逐层初始化存储属性。阶段 2:从父类到子类逐层自定义。确保在调父类 init 前所有存储属性有确定值,避免未初始化状态被父类方法访问。
📖 小节
- Swift 只支持单继承,一个子类只能继承自一个父类
override关键字重写父类的方法、计算属性和下标super在重写中调用父类的实现final阻止类被继承或方法/属性被重写- 多态允许父类引用调用子类的重写实现,实现"一个接口多种行为"
- 子类初始化分两阶段:先子类后父类完成存储属性初始化
📝 作业
- 基础题: 创建一个
Person基类(属性:name、age,方法:introduce()),再创建Student和Teacher子类,分别重写introduce()方法并添加各自的属性(studentId、subject)。 - 进阶题: 设计一个
MediaItem基类,包含title属性和play()方法。创建Movie(额外:director)和Song(额外:artist)子类重写play()。用多态数组统一播放。 - 挑战题: 实现一个「权限控制系统」:基类
User有permissionLevel属性和canAccess(_:)方法。创建AdminUser、EditorUser、ViewerUser子类,AdminUser使用final方法override func canAccess(_:)始终返回 true。使用多态数组统一检查不同用户的访问权限。