Swift类型推断
hudson 译 原文
Swift 是一种静态类型语言,这意味着我们声明的每个属性、常量和变量的类型都需要在编译时指定。然而,通常情况下,这不是必须手动完成的事情,相反,编译器能够根据被分配的值自动推断出各种类型信息 — 这得益于 Swift 支持类型推断的特性。
因此,例如,这里我们声明了几个常量 —— 完全没有指定任何类型,因为编译器能够根据被分配的值推断出信息:
1 | swift复制代码let number = 42 |
为了对比,以下是如果我们手动指定每个常量的类型时的情况:
1 | swift复制代码let number: Int = 42 |
因此,为使Swift 语法尽可能轻便,类型推断起着重要作用,类型推断不仅适用于变量声明和其他类型的赋值语句,而且在许多其他类型的情况下也是如此。
例如,这里我们定义了一个枚举,描述了各种联系人类型,以及一个函数,可以加载属于特定类型的Contact
值数组:
1 | swift复制代码enum ContactKind { |
虽然通常会通过同时指定类型和成员(例如 ContactKind.friend
)来引用上述枚举的成员,但由于类型推断,当在已知类型的上下文中引用枚举成员时,可以完全省略类型的名称 —— 就像在调用上述函数时那样:
1 | swift复制代码let friends = loadContacts(ofKind: .friend) |
真正酷的是,上述的“点语法”不仅适用于枚举成员,在引用任何静态属性或方法时也是如此。例如,这里我们对Foundation 的URL
类进行扩展,增加了一个静态属性 ,用于创建一个指向这个网站的 URL
:
1 | swift复制代码extension URL { |
现在,当调用任何接受URL
参数的方法时(例如新的 Combine框架增强的 URLSession
API),我们可以简单地引用上述属性:
1 | swift复制代码let publisher = URLSession.shared.dataTaskPublisher(for: .swiftBySundell) |
非常棒!然而,虽然类型推断是一种非常有用的特性,但仍然存在一些情况,可能需要额外指定一些类型信息以实现想要的结果。
这些情况中,一个非常常见例子是处理数值类型。当将数值文字分配给变量或常量时,它默认会被推断为 Int
类型 —— 这是一个完全合理的默认值 —— 但如果希望使用其他数值类型,比如 Double
或 Float
,就需要手动指定这些类型。以下是几种方法:
1 | swift复制代码let int = 42 |
还有一种情况是,在调用具有泛型返回类型的函数时,也可能需要给编译器提供额外的类型信息 。
例如,这里我们扩展了内置的 Bundle
类,
增加一个泛型方法,该方法能够轻松加载和解码应用程序中捆绑的任何 JSON 文件:
1 | swift复制代码extension Bundle { |
要了解更多关于 Swift 内置的错误处理机制(上面代码中,通过使用
throws
和try
关键字 )的信息,请查阅关于错误处理的基础文章 。
现在假设在应用程序开发中,在真实服务器和网络代码准备就绪之前,我们希望从捆绑的 JSON 文件中解码以下 User
类型的实例:
1 | swift复制代码struct User: Codable { |
然而,如果像这样调用decodeJSONFile
方法,将会得到一个编译器错误:
1 | swift复制代码// 错误:无法推断泛型参数 ‘T’ |
这是因为即将解码的任何给定的 JSON 文件的确切类型取决于泛型类型 T 在每个调用点上实际引用的内容 —— 而由于我们在上面没有给编译器提供任何此类信息,所以将得到一个错误。在这种情况下,编译器无法知道我们希望解码那种类型User
实例。
要解决这个问题,可以使用与上面用于指定不同类型数值的技术相同的技术,要么给我们的 user
常量一个明确的类型,要么使用 as
关键字 —— 就像这样:
1 | swift复制代码let user: User = try Bundle.main.decodeJSONFile(named: “user-mock”) |
然而,如果在一个已知期望的返回类型的上下文中调用decodeJSONFile 方法
,那么可以再次让 Swift 的类型推断机制找到该信息 —— 就像在下面这种情况下那样,我们定义了一个名为 MockData
的包装器结构,该结构具有一个 User
类型的属性,我们将结果赋值给这个属性:
1 | swift复制代码struct MockData { |
这就是对 Swift 类型推断能力的简要介绍。值得指出的是,类型推断确实有与之相关的计算成本,但幸运的是,这些成本完全发生在编译时(因此不会影响应用程序的运行时性能),但在处理更复杂的表达式时,仍然值得注意。如果遇到一个需要编译器很长时间才能弄清楚的表达式,那么我们总是可以使用上面的任何一种技术来手动指定这些类型。
谢谢阅读! 🚀
本文转载自: 掘金