目录
前言
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型,结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
1.结构体的声明
以我们 “人” 为例子,我们每个人都有自己的名字和年龄。我们可以将“人”抽象为一类数据类型,其包括 名字 和 年龄 连个属性。在GO语言中,使用 type
和 struct
关键字定义结构体,我们可以将 “人” 这个数据类型使用 结构体 定义如下:
type Person struct {
Name string // 名字
Age int // 年龄
}
- 字段命名:每个字段名(属性,即上述代码中的Name,Age)需唯一,同一个结构体中不能有重复的字段名,但每个字段名对应的数据类型可以是任意类型。
2.结构体的初始化
接下来的章节都以上述定义的Person结构体为例子,主要有以下3中初始化的方法:
- 字面量初始化
// 按顺序赋值(需全部字段)
p1 := Person{"Alice", 30}
// 指定字段名赋值(可省略部分字段,未指定字段为零值)
p2 := Person{Name: "Bob", Age: 25}
- 使用 new 函数:返回指针,字段初始化为零值。
p3 := new(Person) // p3 是 *Person 类型
p3.Name = "Charlie"
- Var声明:初始化为零值结构体。
var p4 Person
p4.Name = "Dave"
3.访问与修改字段
通过点号 . 操作符访问或修改字段:
fmt.Println(p1.Name) // 输出 p1的Name属性的值,即"Alice"
p2.Age = 26 // 修改p2的 Age 字段
4.结构体作为函数参数
- 值接收者:操作结构体的副本,不影响原对象。
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
- 指针接收者:操作实际对象,可修改字段。
func (p *Person) SetAge(newAge int) {
p.Age = newAge
}
5.结构体的比较
- 可比较性:若所有字段均为可比较类型(如基本类型、数组、指针等),则结构体可比较。
p1 := Person{"Alice", 30}
p2 := Person{"Alice", 30}
fmt.Println(p1 == p2) // true
- 不可比较情况:若包含不可比较字段(如切片、映射),编译时报错。
type Data struct {
Info map[string]int // 包含 map,结构体不可比较
}
6.匿名字段
- 匿名字段没有显式名称,仅指定类型:
type Data struct {
int
string
}
- 访问时直接使用类型名:
d := Data{42, "answer"}
fmt.Println(d.int) // 42
7.结构体的嵌入
-
Go通过嵌入其他结构体实现组合,而非继承。我们以公司当中的员工为例,员工也是人(Person),所以人(Person)的属性一定都是员工的属性,此外员工还有“职称”的属性,定义如下:
type Employee struct {
Person // 嵌入 Person 结构体(匿名字段)
JobTitle string
}
- 嵌入字段的字段可直接访问:
e := Employee{Person{"Eve", 30}, "Engineer"}
fmt.Println(e.Name) // 直接访问嵌入的 Person.Name
8.结构体标签(Tags)
- 标签是附加到字段的元数据,可通过反射读取,常用于序列化。
type User struct {
Name string `json:"name" xml:"name"`
Age int `json:"age"`
}
- 使用 reflect 包解析标签:
t := reflect.TypeOf(User{})
field := t.Field(0)
fmt.Println(field.Tag.Get("json")) // 输出 "name"
关于结构体标签和反射机制的内容,详情看见GO语言学习(15)结构体标签与反射机制