第 8 章 结构体
结构体(struct) 是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。学过 C 或 C++ 的人都知道结构体,但在 Go 中,没有像 C++ 中的 class
类的概念,只有 struct
结构体的概念,所以也没有继承。
8.1 结构体的声明
在 Go 语言 中使用下面的语法是对结构体的声明。
1 2 3 4 5
| type struct_name struct { attribute_name1 attribute_type attribute_name2 attribute_type ... }
|
例如下面是定义一个名为 Lesson(课程)
的结构体。
1 2 3 4 5
| type Lesson struct { name string target string spend int }
|
上面的代码声明了一个结构体类型 Lesson
,它有 name
、 target
和 spend
三个属性。可以把相同类型的属性声明在同一行,这样可以使结构体变得更加紧凑,如下面的代码所示。
1 2 3 4
| type Lesson2 struct { name, target string spend int }
|
上面的结构体 Lesson
称为 命名的结构体(Named Structure) 。我们创建了名为 Lesson
的新类型,而它可以用于创建 Lesson
类型的结构体变量。
声明结构体时也可以不用声明一个新类型,这样的结构体类型称为 匿名结构体(Anonymous Structure) 。
1 2 3 4
| var Lesson3 struct { name, target string spend int }
|
上面的代码创建了一个匿名结构体 lesson
。
8.2 创建命名的结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func main() { lesson1 := Lesson{ name: "《Go语言极简一本通》", target: "学习Go语言,并完成一个单体服务", spend: 5, } lesson2 := Lesson{"《Go语言极简一本通》", "学习Go语言,并完成一个单体服务", 5}
fmt.Println("lesson1 ", lesson1) fmt.Println("lesson2 ", lesson2) }
|
上面的例子使用了两种方法创建了结构体,第一种是在创建结构体时使用字段名对每个字段进行初始化,而第二种方法是在创建结构体时不使用字段名,直接按字段声明的顺序对字段进行初始化。
8.3 创建匿名结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import "fmt"
func main() { lesson3 := struct { name, target string spend int }{ name: "Go语言微服务架构核心22讲", target: "掌握GO语言微服务方法论,全方位了解每个组件的作用", spend: 3, }
fmt.Println("lesson3 ", lesson3) }
|
8.4 结构体的零值(Zero Value)
当定义好的结构体没有被显式初始化时,结构体的字段将会默认赋为相应类型的零值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func main() { var lesson4 = Lesson{}
fmt.Println("lesson4 ", lesson4) }
|
8.5 初始化结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func main() { var lesson5 = Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", }
fmt.Println("lesson5 ", lesson5) }
|
8.6 访问结构体的字段
点操作符 .
用于访问结构体的字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main
import "fmt"
type Person struct { name, gender string age int }
func main() { var lesson6 = Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", spend: 50, }
fmt.Println("lesson6 name: ", lesson6.name) fmt.Println("lesson6 target: ", lesson6.target) fmt.Println("lesson6 spend: ", lesson6.spend) }
|
当然,使用点操作符 .
可以用于对结构体的字段的赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func main() { var lesson7 = Lesson{} lesson7.name = "从0到Go语言微服务架构师" lesson7.target = "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。" lesson7.spend = 50 fmt.Println("lesson7 ", lesson7) }
|
8.7 指向结构体的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func main() { lesson8 := &Lesson{"从0到Go语言微服务架构师", "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", 50} fmt.Println("lesson8 name: ", (*lesson8).name) fmt.Println("lesson8 name: ", lesson8.name) }
|
在上面的程序中, lesson8
是一个指向结构体 Lesson
的指针,上面用 (*lesson8).name
访问 lesson8
的 name
字段,上面的 lesson8.name
代替 (*lesson8).name
的解引用访问。
Tip:
- 注意:学过 C 语言的同学会认为
lesson8->name
才是正确的访问形式,但是在 Go 语言中,没有->访问的形式,访问结构体中的字段统一都是用.
操作符
8.8 匿名字段
在创建结构体时,字段可以只有类型没有字段名,这种字段称为 匿名字段(Anonymous Field) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
type Lesson4 struct { string int }
func main() { lesson9 := Lesson4{"从0到Go语言微服务架构师", 50} fmt.Println("lesson9 ", lesson9) fmt.Println("lesson9 string: ", lesson9.string) fmt.Println("lesson9 int: ", lesson9.int) }
|
上面的程序结构体定义了两个匿名字段,虽然这两个字段没有字段名,但匿名字段的名称默认就是它的类型。所以上面的结构体 Lesoon4
有两个名为 string
和 int
的字段。
8.9 嵌套结构体
结构体的字段也可能是另外一个结构体,这样的结构体称为 嵌套结构体(Nested Structs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import "fmt"
type Author struct { name string wx string }
type Lesson5 struct { name,target string spend int author Author }
func main() { lesson10 := Lesson5{ name: "从0到Go语言微服务架构师", spend: 50, } lesson10.author = Author{ name: "欢喜哥", wx: "write_code_666", } fmt.Println("lesson10 name:", lesson10.name) fmt.Println("lesson10 spend:", lesson10.spend) fmt.Println("lesson10 author name:", lesson10.author.name) fmt.Println("lesson10 author wx:", lesson10.author.wx) }
|
上面的程序 Lesson5
结构体有一个字段 author
,而且它的类型也是一个结构体 Author
。
8.10 提升字段
结构体中如果有匿名的结构体类型字段,则该匿名结构体里的字段就称为 提升字段(Promoted Fields) 。这是因为提升字段就像是属于外部结构体一样,可以用外部结构体直接访问。就像刚刚上面的程序,如果我们把 Lesson
结构体中的字段 author
直接用匿名字段 Author
代替, Author
结构体的字段例如 name
就不用像上面那样使用 lesson10.author.wx
访问,而是使用 lesson10.wx
就能访问 Author
结构体中的 wx
字段。现在结构体 Author
有 name
、 wx
两个字段,访问字段就像在 Lesson
里直接声明的一样,因此我们称之为提升字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package main
import "fmt"
type Author struct { name string wx string }
type Lesson6 struct { name,target string spend int Author }
func main() { lesson10 := Lesson6{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", } lesson10.author = Author{ name: "欢喜哥", wx: "write_code_666", } fmt.Println("lesson10 name:", lesson10.name) fmt.Println("lesson10 target:", lesson10.target) fmt.Println("lesson10 author wx:", lesson10.wx) }
|
8.11 结构体比较
如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 ==
或 !=
运算符进行比较。可以通过==运算符或 DeeplyEqual()函数比较两个结构相同的类型并包含相同的字段值。因此下面两个比较的表达式是等价的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package main
import "fmt"
type Lesson struct{ name,target string spend int }
func main() { lesson11 := Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", } lesson12 := Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", } fmt.Println(lesson11.name == lesson12.name && lesson11.target == lesson12.target) fmt.Println(lesson11 == lesson12) }
|
8.12 给结构体定义方法
在 Go 中无法在结构体内部定义方法,这一点与 C 语言类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
import "fmt"
type Lesson struct { name, target string spend int }
func (l Lesson) ShowLessonInfo() { fmt.Println("name:", l.name) fmt.Println("target:", l.target) }
func main() { lesson13 := Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", } lesson13.ShowPersonInfo() }
|
上面的程序中定义了一个与结构体 Lesson
绑定的方法 ShowLessonInfo()
,其中 ShowLessonInfo
是方法名, (l Lesson)
表示将此方法与 Lesson
的实例绑定,这在 Go 语言中称为接收者,而 l
表示实例本身,相当于 Python 中的 self
,在方法内可以使用 实例本身.属性名称
来访问实例属性。
8.13 方法的参数传递方式
如果绑定结构体的方法中要改变实例的属性时,必须使用指针作为方法的接收者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package main
import "fmt"
type Lesson struct { name,target string spend int }
func (l Lesson) ShowLessonInfo() { fmt.Println("name:", l.name) fmt.Println("target:", l.target) }
func (l *Lesson) AddTime(n int) { l.spend = l.spend + n }
func main() { lesson13 := Lesson{ name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。", spend:50, } fmt.Println("添加add方法前") lesson13.ShowLessonInfo() lesson13.AddTime(5) fmt.Println("添加add方法后") lesson13.ShowLessonInfo() }
|
如何学习Go语言微服务,快速步入架构师
添加微信 |
公众号更多内容 |
 |
 |