今天,我们通过对类型注释的学习来完成复杂数据类型的下半部分。类型注释是Swift让我们确切地决定每个变量或常量应该是什么数据类型的方式。
今天的内容可能有点少,但是却很重要,通过对类型注释的补完,我们将完成对数据类型的最后一个关键内容。
如何使用类型注释
在前面的学习中,我们已经知道Swift会智能地根据我们分配给它的内容来获取常量或变量应该持有哪种数据类型。然而,有时候我们可能并不想立即分配一个值给常量或变量,或者有时我们想覆盖Swift的类型选择,这时就需要用到类型注释。
到目前为止,我们一直在这样声明常量或变量:
let surname = "Lasso"
var score = 0
Swift使用类型推理可以推断出surname是一个字符串String,因为我们为它分配了文本,然后推断出score是一个整型Int,因为我们为它分配了一个整数。
类型注释让我们明确我们想要的哪些数据类型,如下所示:
let surname: String = "Lasso"
var score: Int = 0
现在我们明确了:surname必须是字符串,score必须是整数。当通常来说,其实我们大可不必按照上面的方式来声明常量和变量,因为Swift会自动推断类型,因为我们给常量和变量赋值的类型中累非常明显。但,有时如果我们给常量和变量的赋值并不明显或者是错误的时候呢?
例如,如果你写成了这样的话:
var score: Double = 0
如果没有类型注释的话,score会被推断为是一个整数类型,但是当我们明确了它的类型注释是Double以后,即使后面的的赋值是0而没有小数点,Swift也依然知道score是一个浮点数,而不是整数。
到目前为止,我们已经知道了几种数据类型,要记住它们的名称很重要,这样你才能在需要的时候使用正确的类型注释:
String 表示字符串
let playerName: String = "Alan"
Int 表示整数
var luckyNumber: Int = 666
Double 表示双精度浮点数
let pi: Double = 3.1415926
Bool 表示布尔值
var isAuthenticated: Bool = true
Array 表示数组,它可以含有许多不同的值,但每个值都必须是同一个数据类型,例如[String]:
var albums: [String] = ["Red", "Fearless"]
Dictionary 表示字典,也是含有学多不同的值,你可以在其中决定如何访问数据,键的数据类型必须是一致的,同样值的数据类型也应一致。例如[String: Int]:
var user: [String: String] = ["homepage": "beautylife.pub"]
Set 表示集合,与数组类似,含有许多不同的值,每个值必须是同一种类型,例如 Set<String>:
var books: Set<String> = Set(["The Bluest Eye", "Foundation", "Girl, Woman, Other"])
当你不想提供初始值时,了解所有这些类型就很重要。例如,这会创建一个字符串数组:
var soda: [String] = ["Coke", "Pepsi", "7UP"]
这其实不用类型注释,因为Swift很明确的知道数组内的数据是一些String。但是,如果你想创建一个空的字符串数组,你必须让Swift知道数据类型,你可以这样写:
var teams: [String] = [String]()
上面的代码,你可以去掉冒号和[String],因为等号后面的内容已经足够让Swift知道这是一个空的字符串数组。另外,你也需要知道,当你在声明一个空的数组、字典和集合时,你需要在后面添加一组括号。
当然,你也可以这样写:
var teams: [String] = []
这个时候,就不能省略类型注释了,因为等号后面没有足够的类型推断数据。
除了这些,还有枚举。枚举与其他类型有些不同,因为它们可以让我们创建自己的新类型,例如包含一周中的天数的枚举、包含用户想要的 UI 主题的枚举,甚至包含当前应用程序中显示的屏幕的枚举。
枚举的值与枚举本身的类型相同,因此我们可以这样写:
enum UIStyle {
case light, dark, system
}
var style = UIStyle.light
这样,Swift 就能在以后的赋值中移除枚举名称,所以我们可以写 style = .dark – 它知道任何新的样式值都必须是某种 UIStyle。
现在,你很有可能会问什么时候应该使用类型注释?其实当我们给常量或变量赋值时,Swift 会自动选择正确的类型,这时往往我们不需要使用类型注释。但请注意,我们赋值的结果往往会关系到Swift类型推断的结果,例如var score = 0 和 var score = 0.0 是完全不同的,这意味着要使用 var score = 0.0 这样的变量,我们会得到一个 Double,而var score = 0 则得到的是一个整数。
最常见的需要类型注释的情况是,我还没有常量的值。你看,Swift 真的很聪明:你可以创建一个还没有值的常量,然后再提供这个值,Swift 会确保在有值之前我们不会意外使用它。它还会确保你只设置一次值,从而保持常量不变。
例如:
let username: String
username = "beautylife"
print(username)
这段代码是合法的:我们是在说 username 将在某个时刻包含一个字符串,并且我们在使用它之前提供了一个值。如果缺少赋值行——username = “beautylife” ——那么 Swift 将拒绝构建我们的代码,因为 username 没有值,同样,如果我们第二次尝试为 username 赋值,Swift 也会警告。
这种代码需要类型注解,因为如果不分配初始值,Swift 就不知道 username 将包含什么样的数据。
无论使用类型推断还是类型注释,都有一条黄金法则: Swift 在任何时候都必须知道常量和变量包含哪些数据类型。这是类型安全语言的核心,可以阻止我们范类似 5 + true 这样的逻辑错误。
重要:虽然类型注释可以让我们在一定程度上覆盖 Swift 的类型推断,但我们完成的代码仍必须是可行的。例如,这样做是不允许的:
let score: Int = "Zero"
Swift 无法将 “Zero”转换为整数,即使有类型注释请求也不行,因此代码无法生成。
为什么Swift要有类型注释?
在学习 Swift 时,人们常问的一个问题是:”为什么 Swift 会有类型注解?”,而紧接着的问题通常是:”什么时候应该在 Swift 中使用类型注解?”
第一个问题的答案主要有三个原因:
- Swift 无法确定应该使用什么类型。
- 你希望 Swift 使用与默认类型不同的类型。
- 你还不想赋值。
第一种情况通常只发生在更高级的代码中。例如,如果您从互联网上加载一些数据,而你知道这些数据恰好是一些专有名词,Swift 无法提前知道这一点,因此你需要告诉它。
随着 Swift 学习的深入,第二种情况会越来越常见,但现在一个简单的例子是创建一个浮点变量,而不必一直在各处写”.0″:
var percentage: Double = 99
这使得 percentage 变成了值为 99.0 的 Double。是的,我们给它分配了一个整数,但我们的类型注解清楚地表明,我们想要的实际数据类型是 Double。
第三种情况是,你想告诉 Swift 某个变量将会存在,但暂时还不想设置它的值。这种情况在 Swift 中很多地方都会发生,看起来就像这样:
var name: String
之后,您可以为 name 指定一个字符串,但不能指定不同的类型,因为 Swift 知道这将是无效的。
当然,这里的第二个问题是 “在 Swift 中什么时候应该使用类型注释?这个问题更加主观,因为答案通常取决于你的个人风格。
在我自己的代码中,我比较推荐尽可能多地使用类型推断。这意味着我不使用类型注释,而是让 Swift 根据我存储在其中的数据来确定事物的类型。我这样做的原因是:
- 它让我的代码更简短、更易读。
- 它允许我只需改变初始值就能改变事物的类型。
其他一些人喜欢始终使用显式类型注解,这也很好,这确实只是一个风格问题。
为什么要创建一个空集合?
刚开始学习 Swift 时,经常会看到这样的例子:
let names = ["Eleanor", "Chidi", "Tahani", "Jianyu", "Michael", "Janet"]
这是一个由六个字符串组成的恒定数组,因为它是恒定的,所以我们不能向数组中添加更多内容。我们在创建数组时就已经知道了所有项目,所以程序的其余部分就是使用这些固定数据。
但有时你并不预先知道所有数据,在这种情况下,更常见的做法是创建一个空的集合,然后在计算数据时添加内容。
例如,我们上面有一个固定的姓名数组,如果我们想找出哪些姓名是以 J 开头的,我们就可以这样做:
- 创建一个名为 jNames 的空字符串数组
- 遍历原始姓名数组中的每个姓名,检查其是否以 “J “开头
- 如果是,则将其添加到 jNames 数组中。
当我们检查完所有名字后,jNames 中就会有两个字符串: Jianyu 和 Janet。当然,如果我们检查的是以 “X “开头的名字,那么数组中就没有名字了,这也没关系。开始是空的,结束也是空的。
在将来,我们将继续学习 Swift 代码,以便真正实现这些功能。
总结:复杂数据类型
我们现在已经超越了简单的数据类型,开始研究如何将它们组合在一起,甚至使用枚举创建我们自己的数据类型。让我们来回顾一下:
- 数组让我们可以在一个地方存储大量值,然后使用整数索引读出它们。数组必须始终是专用的,这样它们才能包含一种特定的类型,而且它们还具有计数、append() 和 contains() 等有用的功能。
- 字典也可以让我们在一个地方存储大量的值,但可以让我们使用指定的键读出这些值。字典必须专用化,以一种特定类型表示键,另一种特定类型表示值,并具有与数组类似的功能,如 contains() 和 count。
- 集合是将大量值存储在一个地方的第三种方法,但我们无法选择存储这些项目的顺序。集合在查找是否包含特定项目时非常有效。
- 枚举让我们可以在 Swift 中创建自己的简单类型,这样我们就可以指定一系列可接受的值,例如用户可以执行的操作列表、我们可以写入的文件类型或要发送给用户的通知类型。
- Swift 必须始终知道常量或变量中的数据类型,并且主要使用类型推断来根据我们分配的数据确定类型。不过,也可以使用类型注释来强制使用特定类型。
在数组、字典和集合中,可以说数组是使用最多的。其次是字典,而集合则排在第三位。这并不意味着集合没有用处,但你会知道什么时候需要它们!