Go 语言有几种用于存储多个元素的数据类型,例如数组(array)和切片(slice);但是,它们不能同时存储不同类型的值 !当我们在 Go 程序中需要组合不同类型的变量时,就应该使用 struct 数据类型。
在本节中,我们将学习 Go 的 struct:如何声明和初始化它们,如何访问其中的数据,以及如何比较两个 struct 是否相等。
什么是 struct?
struct(结构体的缩写)是一种数据类型,它允许我们将不同类型的元素组合在一起,例如整数、字符串、map,甚至其他 struct,形成一个单独的类型。任何具有一系列属性的现实世界实体都可以用 struct 来表示。
如果你熟悉 Java 或 C++,struct 可以与这些面向对象语言中的类(class)相比较。Go 的 struct 可以被视为一种轻量级类(lightweight class),它不支持继承,但支持组合(composition)。
声明 struct
下面我们声明一个 Animal 结构体:
go
type Animal struct {
Name string
Class string
Emoji string
avgLifespan int
Domestic bool
}
解释代码
在这段代码中,type 关键字用于定义新的结构体类型;紧接着是结构体名称 Animal,然后是 struct 关键字。大括号 {} 内部是字段列表,每个字段都有名字和类型。
我们还可以将相同类型的多个字段写在一行:
go
type Animal struct {
Name, Class, Emoji string // 一行定义所有 string 类型字段
avgLifespan int
Domestic bool
}
初始化 struct
声明了 Animal struct 之后,我们可以给它初始化数据,也可以访问它的字段。常见的初始化方式有三种:
1. 基本初始化
go
var crocodile Animal
解释代码
这会创建一个 Animal 类型的局部变量 crocodile,所有字段会被设置为默认值(字符串为空,int 为 0,bool 为 false)。
2. 使用 new 函数
go
crocodile := new(Animal)
解释代码
new 函数为 struct 分配内存,将字段设置为默认值,并返回一个指向 struct 的指针(*Animal)。
3. 使用 struct 字面量
go
crocodile := Animal{
Name: "Crocodile",
Class: "Reptile",
Emoji: "🐊",
avgLifespan: 55,
Domestic: false,
}
fmt.Printf("%#v", crocodile)
// 输出:main.Animal{Name:"Crocodile", Class:"Reptile", Emoji:"🐊", avgLifespan:55, Domestic:false}
解释代码
通过 struct 字面量,我们可以直接给每个字段赋值,初始化 struct。
访问和赋值 struct 字段
初始化 struct 后,可以使用 . 操作符访问字段:
go
fmt.Println("Animal name:", crocodile.Name)
fmt.Println("Class:", crocodile.Class)
fmt.Println("Emoji representation:", crocodile.Emoji)
fmt.Println("Average lifespan in years:", crocodile.avgLifespan)
fmt.Println("Is this animal domestic?", crocodile.Domestic)
// 输出:
// Animal name: Crocodile
// Class: Reptile
// Emoji representation: 🐊
// Average lifespan in years: 55
// Is this animal domestic? false
同样,也可以对已初始化的 struct 单独赋值:
go
var octopus Animal // 声明并初始化新的 Animal 类型 struct
octopus.Name = "Octopus"
octopus.Class = "Cephalopod"
octopus.Emoji = "🐙"
struct 的比较与相等性
如果两个 struct 属于同一类型,并且所有字段的值相等,它们可以使用 == 运算符进行比较。示例:
go
type Person struct {
Name string
Age int
}
// 声明并初始化三个 Person struct
jerry1 := Person{Name: "Jerry", Age: 37}
jerry2 := Person{Name: "Jerry", Age: 37}
jerry3 := Person{Name: "Jerry"} // jerry3 的 Age 字段未初始化
fmt.Printf("structs: jerry1 and jerry2 are equal is %t\n", jerry1 == jerry2)
fmt.Printf("structs: jerry1 and jerry3 are equal is %t", jerry1 == jerry3)
// 输出:
// structs: jerry1 and jerry2 are equal is true
// structs: jerry1 and jerry3 are equal is false
解释代码
虽然 jerry1、jerry2 和 jerry3 都是 Person 类型,但 jerry3 的 Age 字段未初始化,因此与 jerry1 比较时结果为 false。
注意,如果 struct 中包含无法用 == 比较的字段(例如 map 或 slice),则该 struct 变量不可比较。
总结
今天我们学习了很多内容,最重要的几点是:
-
struct 的定义及初始化方法。
-
如何访问或赋值 struct 的单个字段。
-
如何比较两个 struct 是否相等。
接下来可以用所学的 struct 知识来解决实际问题。