扩展中的默认实现
通过协议扩展提供共享行为。
扩展中的默认实现 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。
问题所在
协议声明要求,但不提供行为。如果每个遵循协议的类型都必须重复相同的逻辑,代码就会重复。协议扩展通过提供默认实现解决了这个问题。
protocol Greeter {
func greet() -> String
}添加默认实现
协议扩展可以实现某项要求。遵循协议的类型会免费获得该行为,除非它们自行覆盖该实现。
protocol Greeter { func greet() -> String }
extension Greeter {
func greet() -> String { "Hello!" }
}
struct Robot: Greeter {}
print(Robot().greet())覆盖默认实现
遵循协议的类型可以提供自己的实现,该实现的优先级高于扩展中的默认实现。
protocol Greeter { func greet() -> String }
extension Greeter { func greet() -> String { "Hello!" } }
struct French: Greeter {
func greet() -> String { "Bonjour!" }
}
print(French().greet())添加额外方法
扩展可以添加协议中完全没有声明的全新方法。每个遵循协议的类型都会自动获得这些方法。
protocol Named { var name: String { get } }
extension Named {
func loudName() -> String { name.uppercased() }
}
struct City: Named { let name: String }
print(City(name: "paris").loudName())默认计算属性
扩展还可以提供计算属性作为默认实现。
protocol Sized {
var width: Double { get }
var height: Double { get }
}
extension Sized {
var area: Double { width * height }
}
struct Rect: Sized { let width: Double; let height: Double }
print(Rect(width: 2, height: 5).area)基于要求构建
默认实现可以调用协议自身的要求,从每种类型必须提供的最小契约出发,组合出更丰富的行为。
protocol Summable {
var values: [Int] { get }
}
extension Summable {
var total: Int { values.reduce(0, +) }
}
struct Cart: Summable { let values: [Int] }
print(Cart(values: [10, 20, 30]).total)受约束的扩展
使用 where 子句,使默认实现仅在满足额外条件时适用。这里的默认实现仅在 Self 同时遵循 Equatable 时存在。
protocol Container {
associatedtype Item
var items: [Item] { get }
}
extension Container where Item: Equatable {
func contains(_ x: Item) -> Bool { items.contains(x) }
}
struct Bag: Container { let items: [Int] }
print(Bag(items: [1, 2, 3]).contains(2))默认实现与要求并存
您可以在协议中声明一个方法 AND 提供默认实现。将方法列入协议后,就可以通过协议类型启用动态分派。
protocol Logger {
func log() -> String
}
extension Logger {
func log() -> String { "default log" }
}
struct A: Logger {}
struct B: Logger { func log() -> String { "B log" } }
let loggers: [Logger] = [A(), B()]
for l in loggers { print(l.log()) }静态分派的注意事项
如果方法 ONLY 出现在扩展中(而不在协议中),即使具体类型定义了自己的实现,通过协议类型值调用时仍会使用扩展版本。要启用动态分派,请在协议中声明该方法。
protocol P {}
extension P { func who() -> String { "P-default" } }
struct Q: P { func who() -> String { "Q" } }
let direct = Q()
let asP: P = Q()
print(direct.who())
print(asP.who())减少样板代码
少量必需内容加上丰富默认实现的组合,是面向协议设计的核心:遵循协议的类型只需实现最少内容,共享行为则集中在一个地方。
protocol Identifiable { var id: Int { get } }
extension Identifiable {
var tag: String { "ID-" + String(id) }
}
struct Order: Identifiable { let id: Int }
print(Order(id: 7).tag)组合多个扩展
您可以将默认实现拆分到多个扩展中,以便组织代码;它们都会为遵循协议的类型提供功能。
protocol Money { var cents: Int { get } }
extension Money { var dollars: Double { Double(cents) / 100 } }
extension Money { var label: String { "$" + String(format: "%.2f", dollars) } }
struct Price: Money { let cents: Int }
print(Price(cents: 1299).label)快速检查
测试您对协议扩展默认实现的掌握程度。
回顾
协议扩展提供默认实现,让遵循协议的类型继承共享行为,并在需要时进行覆盖。您可以添加额外方法和计算属性,使用 where 限制默认实现的适用条件;还学习了静态分派与动态分派的规则:要启用动态分派,请在协议中声明方法。
常见问题解答
「扩展中的默认实现」课时是免费的吗?
是的 — 「扩展中的默认实现」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。
「扩展中的默认实现」这节课中我会学到什么?
通过协议扩展提供共享行为。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「扩展中的默认实现」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。