购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.5 结构体类型

映射用来存储某些数据很方便,不过,它也有一些局限性。Go中的映射不支持键的类型限制,因此没有定义任何API。这意味着任何类型的数据都可以用作映射中的键。同样,映射中所有的值必须是同一类型的。基于这些原因,映射不是函数之间传递数据的理想方式。当你有相关数据想要组合在一起时,你应该定义一个结构体。

如果你已经了解一种面向对象的语言,你可能会好奇类和结构体之间的区别。区别很简单:Go语言没有类,因为它没有继承。这并不意味着Go没有面向对象语言的一些特性,它只是以一种稍微不同的方式实现。你将在第7章了解更多关于Go的面向对象特性。

大多数语言都有类似于结构体的概念,而且Go语言读写结构体的语法应该看起来很熟悉:

用关键字type、结构体类型的名称、关键字struct以及一对大括号{}来定义一个结构体类型。在大括号内,你列出结构体的字段。就像你在变量声明中先写变量名然后写变量类型一样,你在结构体里先写字段名再写字段类型。另外注意,与映射字面量不同,结构体声明中的字段之间没有逗号分隔。你可以在函数内部或外部定义一个结构体类型。在一个函数内部定义的结构体类型只能在该函数内部使用。(你将在第5章了解更多关于函数的内容。)

从技术上讲,你可以把结构体定义限定在任何块级作用域内。你将在第4章了解更多关于块的内容。

一旦声明了一个结构体类型,你就可以定义该类型的变量:

这里我们使用了var声明。由于没有给fred赋值,因此它现在的值是person结构体类型的零值。一个零值结构体的每个字段都被设置为该字段的零值。

结构体字面量也可以直接赋值给变量:

与映射不同,赋予空结构体字面量与根本不赋值没有区别。两者都会将结构体中的所有字段初始化为零值。存在两种非空结构体字面量的风格。首先,结构体字面量可以指定为花括号内的字段值的逗号分隔列表:

当使用这种结构体字面量格式时,必须为结构体中的每个字段指定一个值,并且这些值将按照它们在结构体定义中声明的顺序被赋给字段。

第二种结构体字面量的风格看起来像映射字面量的风格:

你可以使用结构体中的字段名来指定值。这种风格有一些优势。它允许你以任意顺序指定字段,而且不需要为所有字段提供值。任何未指定的字段都将被设置为零值。

你不能混合使用两种结构体字面量风格:要么所有字段都使用名称显式命令,要么都不使用字段名称。对于总是指定所有字段的小型结构体,简单的结构体字面量风格就足够好了。在其他情况下,请使用字段名称。虽然这样更冗长,但它明确表示哪个值被赋给了哪个字段,而无须参考结构体的定义。它也更易于维护。如果你在初始化结构体时没有使用字段名称,并且结构体在未来的版本中增加了额外的字段,你的代码将会编译失败。

通过点号标记方式访问结构体中的字段:

和使用中括号读取写入映射类似,你使用点标记方式读取和写入结构体字段。

3.5.1 匿名结构体

你也可以在不首先给结构体类型命名的情况下声明一个变量来实现一个结构体类型。这称为匿名结构体:

在这个例子中,变量person和pet的类型是匿名结构体。你可以像对待具名结构体类型一样,对匿名结构体中的字段进行赋值(和读取)。就像你可以使用结构体字面量初始化具名结构体的实例一样,你也可以对匿名结构体进行相同的操作。

你可能会想知道,将数据类型与单个实例关联起来何时是有用的。匿名结构体在两种常见情况下很方便。第一种是当你将外部数据转换为结构体或将结构体转换为外部数据(如JSON或Protocol Buffers)时,这分别称为解组数据和编组数据。你将在13.3节中学习如何做到这一点。

编写测试的时候匿名结构体也经常出现。你将在第15章编写表格驱动测试时使用匿名结构体切片。

3.5.2 比较和转换结构体

结构体是否可比较取决于结构体的字段。完全由可比较类型组成的结构体是可比较的;包含切片或映射字段的结构体则是不可比较的(正如你将在后面的章节中看到的,函数和管道类型的字段也会导致结构体不可比较)。

与Python或Ruby不同,在Go中并不存在能够覆盖相等性判断的魔法函数,使得==和!=可以处理不可比较的结构体。当然,你可以编写自己的函数来比较结构体。

正如Go不允许对不同原始类型的变量进行比较一样,Go也不允许在代表不同类型的结构体变量之间进行比较。如果两个结构体的字段具有相同的名称、顺序和类型,Go允许你将一个结构体类型转换为另一个结构体类型。让我们一探究竟。比如给定下面的结构体:

你可以使用类型转换将firstPerson的实例转换为secondPerson的实例,但你不能使用==来比较firstPerson的实例和secondPerson的实例,因为它们是不同的类型。

你不能将firstPerson的一个实例转换为thirdPerson,因为它们字段的顺序不同:

你不能将firstPerson的一个实例转换为fourthPerson,因为字段名不匹配:

最后,你不能将firstPerson的一个实例转换为fifthPerson,因为存在一个额外的字段:

匿名结构体增加了一个小的变化点:当比较两个结构体变量时,如果至少有一个的类型是匿名结构体,那么只要两个结构体的字段名称、顺序和类型都相同,就可以不经过类型转换而直接进行比较。并且因为这两个结构体的字段具有相同的名称、顺序和类型,所以也可以在具名结构体和匿名结构体之间进行相互赋值: DfeL04vV50jiLIS2Zcla0zLhmW4ceije4Aoy/aOyfZQVZtaAZFpxgwn2LZTGz4u1

点击中间区域
呼出菜单
上一章
目录
下一章
×