引入
map 能用键存取数据,但有时候想把名字、年龄、地址这些不同类型的数据组合成一个整体。Go 的结构体就是用来做这件事的,把一组相关数据打包成一个自定义类型。
正文
定义
定义
结构体用来把一组相关数据组合在一起,是一个自定义类型。结构体由多个字段组成,每个字段有名字和类型。结构体是值类型,赋值和传参会复制整个结构体。
语法
// 定义结构体类型
type Person struct {
Name string
Age int
}
// 创建实例
p := Person{Name: "Tom", Age: 20}例子
定义和初始化
type User struct {
Name string
Age int
Email string
}
// 按字段名初始化(推荐)
u1 := User{Name: "Tom", Age: 20, Email: "[email protected]"}
// 按顺序初始化(不推荐,字段顺序变了就出错)
u2 := User{"Amy", 22, "[email protected]"}
// 部分初始化,其余为零值
u3 := User{Name: "Bob"}
fmt.Println(u3) // {Bob 0 }按字段名初始化最安全,不用管顺序。
零值
var u User
fmt.Println(u) // { 0 }结构体的零值是每个字段取各自的零值。
访问和修改字段
u := User{Name: "Tom", Age: 20}
fmt.Println(u.Name) // Tom
fmt.Println(u.Age) // 20
u.Age = 25
fmt.Println(u.Age) // 25用 . 访问和修改字段。
结构体指针
u := User{Name: "Tom", Age: 20}
p := &u
fmt.Println(p.Name) // Tom(自动解引用)
p.Age = 30
fmt.Println(u.Age) // 30结构体指针访问字段用 .,Go 自动解引用,不需要写 (*p).Name。
嵌套结构体
type Address struct {
City string
Zip string
}
type Person struct {
Name string
Address Address
}
p := Person{
Name: "Tom",
Address: Address{City: "Beijing", Zip: "100000"},
}
fmt.Println(p.Address.City) // Beijing结构体的字段可以是另一个结构体,用 . 逐层访问。
匿名字段(嵌入)
type Animal struct {
Name string
}
type Dog struct {
Animal
Breed string
}
d := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Labrador",
}
fmt.Println(d.Name) // Buddy(直接访问嵌入字段)
fmt.Println(d.Animal.Name) // Buddy(完整路径也行)匿名字段没有字段名,外部结构体可以直接访问内部结构体的字段,像继承一样。
匿名字段的方法提升
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return a.Name + " makes a sound"
}
type Dog struct {
Animal
}
d := Dog{Animal: Animal{Name: "Buddy"}}
fmt.Println(d.Speak()) // Buddy makes a sound嵌入结构体的方法会被提升到外部结构体,可以直接调用。
结构体比较
type Point struct {
X, Y int
}
a := Point{1, 2}
b := Point{1, 2}
c := Point{3, 4}
fmt.Println(a == b) // true
fmt.Println(a == c) // false所有字段都可比较的结构体可以用 == 比较。
结构体标签
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"-"`
}标签是给反射库用的元数据,比如 json 序列化时控制字段名和忽略字段。
常见写法
| 写法 | 说明 |
|---|---|
type T struct{…} | 定义结构体类型 |
T{Field: v} | 按字段名初始化 |
new(T) / &T{} | 创建结构体指针 |
t.Field | 访问字段 |
t.Field = v | 修改字段 |
p.Field | 指针访问字段(自动解引用) |
T{OtherStruct} | 匿名字段嵌入 |
特点
- 结构体用来把相关数据打包成一个自定义类型
- 结构体是值类型,赋值和传参会复制
- 用
&取地址得到指针,指针访问字段用.自动解引用 - 匿名字段实现组合,外部结构体直接访问内部的字段和方法
- 所有字段可比较的结构体支持
==比较 - 结构体标签用于反射,常见于 JSON、ORM 等场景
- 配合 Go 方法 可以给结构体绑定行为,实现面向对象
理解
结构体就是 Go 版的”对象”,把名字、年龄这些散的数据打包成一个整体。字段可以嵌套,可以嵌入,配合方法就能定义复杂的数据结构。Go 没有类,结构体就是最接近类的东西。
引出
结构体能组合数据,那想给不同类型定义统一的行为怎么办?接下来看 Go 接口,学习怎么用接口定义一组方法签名,让不同类型实现同一套行为。