Menu
CoddyTech

协议扩展

本课属于 Coddy Swift 学习旅程的“面向对象编程”单元。第 29 课,共 57 课。

协议扩展为协议的方法提供默认实现。所有遵循该协议的类型都可以免费获得这个实现,而且类型仍然可以提供自己的实现:

protocol Greeter {
    var name: String { get }
    func greet() -> String
}

extension Greeter {
    func greet() -> String { "Hello, \(name)!" }
}

struct English: Greeter { let name: String }
struct Pirate: Greeter {
    let name: String
    func greet() -> String { "Ahoy, \(name)!" }
}

print(English(name: "Ada").greet())
print(Pirate(name: "Ada").greet())

输出:

Hello, Ada!
Ahoy, Ada!

扩展还可以添加仅根据要求构建的新方法。符合要求的类型只需编写少量必需部分,就能获得其余功能:

protocol Scored {
    var points: [Int] { get }
}

extension Scored {
    var total: Int { points.reduce(0, +) }
    var best: Int { points.max() ?? 0 }
    func summary() -> String { "total \(total), best \(best)" }
}

struct Player: Scored {
    let points: [Int]
}

print(Player(points: [3, 9, 4]).summary())

输出:

total 16, best 9

这让结构体、类和枚举可以共享行为,而无需继承。标准库的工作方式也一样:每个 Collection 都会通过协议扩展获得 isEmpty、first 和 map。

extension Collection {
    // isEmpty、first、map、filter 等都只需在这里定义一次
    // 每个数组、集合、字典和字符串都能使用它们
}

受约束的扩展只为符合条件的类型添加方法,使用 where 编写:

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

extension Container where Item == Int {
    var sum: Int { items.reduce(0, +) }
}

struct Box: Container {
    var items: [Int]
}

print(Box(items: [2, 5]).sum)

输出:

7
challenge icon

挑战

简单

声明一个协议 Priced,要求包含 var price: Int { get }(以分为单位)和 var formatted: String { get }。在协议扩展中,为 formatted 提供一个默认实现,返回 $4.50,并添加一个方法 isAffordable(budget:),用于判断价格是否在预算范围内。让结构体 Coffee(价格为 450)和 Sandwich(包含一个存储属性 price)遵循该协议。Coffee 必须将默认的 formatted 替换为 coffee $4.50。

提供的代码会读取预算,然后读取内容为 coffee 或 sandwich,650 的行。对于每件商品,代码会输出 formatted,以及 yes 或 no。

你的代码应写在 Priced.swift、Coffee.swift 和 Sandwich.swift 中。main.swift 包含提供的输入/输出代码,不能编辑。

亲手试试

// 提供的输入/输出代码:保持原样
var input: [String] = []
while let line = readLine() { input.append(line) }
let budget = Int(input[0])!
for line in input.dropFirst() {
    let item: any Priced = line == "coffee" ? Coffee() : Sandwich(price: Int(line.split(separator: ",")[1])!)
    print("\(item.formatted) \(item.isAffordable(budget: budget) ? "yes" : "no")")
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Swift 在线编辑器