Generic(泛型编程)——是 Swift 的一种机制,允许编写带有参数化类型
要点
Generic(泛型)——是 Swift 的一种语法构造,允许编写灵活、可复用的代码,带有类型参数化。不是使用具体类型,而是在尖括号中写入占位符——通常是
如果没有泛型,就需要为每种类型编写单独的函数:swapInts、swapStrings、swapDates。泛型函数 swap 用一条声明取代了所有三个变体。这减少了重复,提高了可读性,并降低了复制代码时出错的可能性。
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
Swift 编译器为每具体泛型使用生成特化代码。这意味着泛型函数 swapValues
泛型函数——是接受一个或多个类型参数的函数。类型参数在函数名后的尖括号中指定:
一个函数可以有多个泛型参数。例如,函数 findKey
func findKey<K: Hashable, V>(
for value: V,
in dictionary: [K: V]
) -> K? where V: Equatable {
for (key, dictValue) in dictionary {
if dictValue == value {
return key
}
}
return nil
}
K: Hashable 和 where V: Equatable 约束确保 findKey 只能使用键可哈希、值可比较的字典调用。这些约束不是官僚主义,而是必要性:没有 Hashable 就无法按键索引搜索,没有 Equatable 就无法将 value 与 dictValue 进行比较。
where——是 Swift 的一个关键字,对泛型参数施加额外约束。与在尖括号中简单指定协议(
where 子句写在函数或类型的签名之后,左花括号之前。在 where 中可以指定 T: Comparable & Hashable(同时两个协议),或 T.U == Int(关联类型的具体化)。
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
}
extension Container where Item: Comparable {
func isSorted() -> Bool {
// Implementation available only if Item: Comparable
return true
}
}
带有 where 的扩展仅为满足条件的类型添加方法。Container
Associated Types(关联类型)——是一种在不指定具体类型的情况下使协议成为泛型的方式,无需在声明阶段指定具体类型。协议声明一个 associatedtype 而不是
使用 where 可以在不同协议的关联类型之间建立联系。例如,可以要求一个协议的 Item 与另一个协议的 Iterator.Element 相匹配。这保证了编译级别的类型兼容性。
protocol StackProtocol {
associatedtype Element
mutating func push(_ item: Element)
mutating func pop() -> Element?
}
struct IntStack: StackProtocol {
typealias Element = Int
private var items: [Int] = []
mutating func push(_ item: Int) { items.append(item) }
mutating func pop() -> Int? { items.popLast() }
}
IntStack 使用 typealias 将 Element 具体化为 Int。编译器检查协议的所有要求是否都满足此类型。如果没有关联类型,就必须使用泛型协议编写 StackProtocol
泛型扩展允许向泛型类型添加带有额外约束的方法。这是“条件遵循”模式,类型仅在特定条件下获得功能。下标也可以是泛型的。
下标可以是泛型的:它在尖括号中接受泛型参数。这对于使用任意索引安全访问集合很有用,当索引可能是不同类型时。
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
}
extension Array {
subscript<Indices: Sequence>(indices: Indices) -> [Element]
where Indices.Element == Int {
return indices.map { self[$0] }
}
}
这种构造提供了强大的组合:sum 方法仅出现在数值数组中,而具有任意索引的下标适用于任何整数索引序列。编译器根据 where 条件自行管理这些成员的可见性。
使用泛型时的错误通常与错误约束或尝试在需要具体类型的地方使用泛型有关。让我们看一下开发人员常遇到的三种情况。
在具体类型足够的地方添加泛型参数——是一种反模式。如果函数始终使用 String 工作,则不需要使其成为
在未指定方法存在的情况下对泛型参数调用方法——是一个错误。如果没有指定 T: Collection,
// ❌ Error: value has no count method
func countElements<T>>(value: T) -> Int { value.count }
// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }
错误是可以理解的:泛型
常见问题解答
Generic——是一种编写代码的方式,适用于任何类型,无需为 Int、String 等类型重复相同的逻辑。不使用具体类型,而是使用占位符
Generic 在编译阶段保留具体类型信息,使编译器能够检查类型并生成优化代码。Any——是类型擦除:可以传递任何值,但类型丢失,需要转换(as?)。
使用尖括号中的 T: SomeProtocol 语法或单独的 where T: SomeProtocol 子句。约束确保泛型参数支持特定的方法和属性。
Associated Type(associatedtype)——是协议内部类型的占位符。协议不指定具体类型,而是声明一个在实现中具体化的 associatedtype:Array 有 Element,Dictionary 有 Key 和 Value。
Swift 将泛型代码编译为每个具体类型的特化版本。实际上,Int 的泛型函数并不比手动编写的 Int 函数慢。抽象没有额外开销。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。