Swift中的泛型和类型化:编写为您工作的代码

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么没有泛型,您的Swift代码很快就会变成意大利面条

\n\n

想象一下,您正在编写一个披萨配送应用程序。您需要处理订单、客户、骑手——每次都要为排序、搜索或过滤编写相同的代码,只改变数据类型?这听起来像是任何开发者的噩梦。正是在这里,Swift中的泛型登场了——这是一个强大的工具,允许您编写灵活、可重用的代码,同时不牺牲类型安全。

\n\n

在本文中,我们不仅仅会快速浏览语法。我们将深入探讨Swift中的类型化和泛型如何协同工作,使您的代码不仅美观,而且可靠。您将学习如何消除重复代码,如何使用带有associatedtype的protocol,以及为什么where子句是您最好的朋友。

\n\n

什么是泛型,为什么它们改变了游戏规则

\n\n

泛型是一种机制,允许您编写适用于任何满足特定条件的数据类型的函数、结构体、类和枚举。您不必为Int、String和Double编写三个版本的函数,而是编写一个适用于任何类型的函数。

\n\n

在Swift中,类型化是严格且静态的。这意味着编译器在编译时检查类型。泛型并不违反这一规则——它们使其更加灵活。编译器在调用函数时自行推断具体类型,但检查仍然存在。

\n\n

第一个示例:没有泛型的函数

\n\n
// 不好:为三种类型编写三个函数\nfunc swapInts(_ a: inout Int, _ b: inout Int) {\n    let temp = a\n    a = b\n    b = temp\n}\n\nfunc swapStrings(_ a: inout String, _ b: inout String) {\n    let temp = a\n    a = b\n    b = temp\n}\n\nfunc swapDoubles(_ a: inout Double, _ b: inout Double) {\n    let temp = a\n    a = b\n    b = temp\n}
\n\n

现在,来看一个使用泛型的优雅解决方案:

\n\n
// 好:一个函数适用于所有类型\nfunc swapValues<T>(_ a: inout T, _ b: inout T) {\n    let temp = a\n    a = b\n    b = temp\n}\n\n// 使用:\nvar x = 10\nvar y = 20\nswapValues(&x, &y) // T = Int\n\nvar name1 = "Alice"\nvar name2 = "Bob"\nswapValues(&name1, &name2) // T = String
\n\n

看到魔法了吗?<T> 是一个类型占位符。编译器在调用时自行替换实际类型。这就是Swift中泛型编程的基础。

\n\n

泛型类型:创建自己的通用栈

\n\n

Swift中的泛型不仅适用于函数,也适用于整个类型。让我们创建一个Stack结构体——经典的LIFO(后进先出)数据结构示例。

\n\n
struct Stack<Element> {\n    private var items: [Element] = []\n    \n    mutating func push(_ item: Element) {\n        items.append(item)\n    }\n    \n    mutating func pop() -> Element? {\n        return items.popLast()\n    }\n    \n    func peek() -> Element? {\n        return items.last\n    }\n    \n    var count: Int {\n        return items.count\n    }\n}\n\n// 使用:\nvar intStack = Stack<Int>()\nintStack.push(1)\nintStack.push(2)\nintStack.push(3)\nprint(intStack.pop() ?? 0) // 3\n\nvar stringStack = Stack<String>()\nstringStack.push("Swift")\nstringStack.push("Generics")\nprint(stringStack.peek() ?? "") // "Generics"
\n\n

请注意:我们在声明结构体时在尖括号中指定了Element类型。在创建实例时,我们将其具体化——Stack<Int>或Stack<String>。编译器保证您不会意外地将字符串放入整数栈中。

\n\n

带有关联类型的协议:协议的泛型

\n\n

在Swift中,协议不能直接拥有泛型参数。取而代之的是使用

博客

书籍推荐