协议组合
使用 & 组合多个协议要求。
协议组合 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。
组合协议
有时一个值必须同时满足多个协议。Swift 允许您使用 & 运算符组合协议:SomeProtocol & AnotherProtocol。
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }组合类型参数
类型为 Named & Aged 的参数可以接受同时遵循这两个协议的任意值。
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
struct Person: Named, Aged {
let name: String
let age: Int
}
func intro(_ x: Named & Aged) {
print(x.name + " is " + String(x.age))
}
intro(Person(name: "Sam", age: 28))为什么不使用继承
组合可以让您在调用处要求同时遵循两个协议,而不必创建新的组合协议。这样可以使协议保持小巧并且易于复用。
protocol Drawable { func draw() -> String }
protocol Resizable { func resize(_ f: Double) -> String }
struct Icon: Drawable, Resizable {
func draw() -> String { "drawing" }
func resize(_ f: Double) -> String { "x" + String(f) }
}
func render(_ v: Drawable & Resizable) {
print(v.draw(), v.resize(2))
}
render(Icon())三个或更多协议
您可以使用额外的 & 运算符链接任意数量的协议。
protocol A { func a() -> String }
protocol B { func b() -> String }
protocol C { func c() -> String }
struct X: A, B, C {
func a() -> String { "a" }
func b() -> String { "b" }
func c() -> String { "c" }
}
func use(_ v: A & B & C) { print(v.a(), v.b(), v.c()) }
use(X())将类与协议组合
您可以将类类型与协议组合:SomeClass & SomeProtocol 要求该值属于该类(或其子类)AND 遵循该协议。
class Vehicle { var speed = 0 }
protocol Electric { var battery: Int { get } }
class Tesla: Vehicle, Electric { var battery = 100 }
func check(_ v: Vehicle & Electric) {
print(v.speed, v.battery)
}
check(Tesla())返回组合类型
函数可以返回组合类型,向调用者保证结果遵循列出的所有协议。
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
struct Person: Named, Aged { let name: String; let age: Int }
func makePerson() -> Named & Aged {
Person(name: "Eve", age: 22)
}
let p = makePerson()
print(p.name, p.age)组合的类型别名
使用 typealias 为组合类型指定一个易读的名称,这样就可以复用它,而不必重复 & 链。
protocol Codable2 { func encode() -> String }
protocol Loggable { func log() -> String }
typealias Persistable = Codable2 & Loggable
struct Record: Persistable {
func encode() -> String { "data" }
func log() -> String { "logged" }
}
func save(_ r: Persistable) { print(r.encode(), r.log()) }
save(Record())集合中的组合类型
您可以将组合类型的值存储在数组中,并访问每个元素所遵循的所有协议成员。
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
struct P: Named, Aged { let name: String; let age: Int }
let group: [Named & Aged] = [P(name: "A", age: 1), P(name: "B", age: 2)]
for g in group { print(g.name, g.age) }与标准协议组合
组合与库中的协议结合使用时尤其有用。例如,可以同时要求遵循 CustomStringConvertible 和 Equatable。
func report<T>(_ items: [T]) where T: CustomStringConvertible & Equatable {
for i in items { print(i.description) }
}
report([1, 2, 3])作为标记的空协议
组合可以将标记协议(没有要求)与功能型协议结合起来,从而在类型系统中表达意图。
protocol Secure {}
protocol Sendable2 { func send() -> String }
struct Packet: Secure, Sendable2 {
func send() -> String { "sent securely" }
}
func transmit(_ p: Secure & Sendable2) { print(p.send()) }
transmit(Packet())组合与泛型的比较
组合(A & B)会擦除具体类型;泛型(<T: A & B>)则会保留它。当您需要关联参数类型和返回类型时,请使用泛型。
protocol A { func a() -> String }
protocol B { func b() -> String }
struct S: A, B { func a() -> String { "a" }; func b() -> String { "b" } }
func generic<T: A & B>(_ x: T) -> T { print(x.a(), x.b()); return x }
_ = generic(S())快速检查
请检查您对协议组合的理解。
回顾
使用 & 进行协议组合,可以同时要求遵循多个协议(以及可选的一个类),而无需定义新的组合协议。您可以使用 typealias 为组合命名,将其用于参数、返回值和集合中;同时还了解了组合与泛型在保留具体类型方面的区别。
常见问题解答
「协议组合」课时是免费的吗?
是的 — 「协议组合」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。
「协议组合」这节课中我会学到什么?
使用 & 组合多个协议要求。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「协议组合」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。