结构体(struct) 是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。学过 C 或 C++ 的人都知道结构体,但在 Go 中,没有像 C++ 中的 class 类的概念,只有 struct 结构体的概念,所以也没有继承。
8.1 结构体的声明
在 Go 语言 中使用下面的语法是对结构体的声明。
type struct_name struct {
    attribute_name1   attribute_type
    attribute_name2   attribute_type
    ...
}
例如下面是定义一个名为 Lesson(课程) 的结构体。
type Lesson struct {
	name   string //名称
	target string //学习目标
	spend  int    //学习花费时间
}
上面的代码声明了一个结构体类型 Lesson ,它有 name 、 target 和 spend 三个属性。可以把相同类型的属性声明在同一行,这样可以使结构体变得更加紧凑,如下面的代码所示。
type Lesson2 struct {
    name, target    string
    spend             int
}
上面的结构体 Lesson 称为 命名的结构体(Named Structure) 。我们创建了名为 Lesson 的新类型,而它可以用于创建 Lesson 类型的结构体变量。
声明结构体时也可以不用声明一个新类型,这样的结构体类型称为 匿名结构体(Anonymous Structure) 。
var Lesson3 struct {
    name, target    string
    spend             int
}
上面的代码创建了一个匿名结构体 lesson 。
8.2 创建命名的结构体
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 创建匿名结构体
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)
当定义好的结构体没有被显式初始化时,结构体的字段将会默认赋为相应类型的零值。
package main
import "fmt"
type Lesson struct {
	name, target    string
	spend             int
}
func main() {
	// 不初始化结构体
	var lesson4 = Lesson{}
	fmt.Println("lesson4 ", lesson4)
}
8.5 初始化结构体
package main
import "fmt"
type Lesson struct {
	name, target    string
	spend             int
}
func main() {
	// 为结构体指定字段赋初值
	var lesson5 = Lesson{
		name: "从0到Go语言微服务架构师",
		target: "全面掌握Go语言微服务如何落地,代码级彻底一次性解决微服务和分布式系统。",
	}
    // 上面的结构体变量 lesson5 只初始化了 name 和 target 字段, spend字段没有初始化,所以会被初始化为零值
	fmt.Println("lesson5 ", lesson5)
}
8.6 访问结构体的字段
点操作符 . 用于访问结构体的字段。
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)
}
当然,使用点操作符 . 可以用于对结构体的字段的赋值。
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 指向结构体的指针
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) 。
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)
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 里直接声明的一样,因此我们称之为提升字段。
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()函数比较两个结构相同的类型并包含相同的字段值。因此下面两个比较的表达式是等价的:
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) // true
	fmt.Println(lesson11 == lesson12) // true
}
8.12 给结构体定义方法
在 Go 中无法在结构体内部定义方法,这一点与 C 语言类似。
package main
import "fmt"
// Lesson 定义一个名为 Lesson 的结构体
type Lesson struct {
	name,target string
	spend int
}
// PrintPersonInfo 定义一个与 Person 的绑定的方法
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 方法的参数传递方式
如果绑定结构体的方法中要改变实例的属性时,必须使用指针作为方法的接收者。
package main
import "fmt"
// Lesson 定义一个名为 Lesson 的结构体
type Lesson struct {
	name,target string
	spend int
}
// ShowLessonInfo 定义一个与 Lesson 的绑定的方法
func (l Lesson) ShowLessonInfo() {
	fmt.Println("name:", l.name)
	fmt.Println("target:", l.target)
}
// AddTime 定义一个与 Lesson 的绑定的方法,使 spend 值加 n
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()
}
联系作者
| 添加微信 | 公众号更多内容 | 
|---|---|
|  |  | 










