跳到主要内容

Go 结构体和方法

本章将深入介绍 Go 语言中的结构体和方法。

结构体定义

基本结构体

type Person struct {
Name string
Age int
City string
}

// 创建实例
p := Person{Name: "张三", Age: 25, City: "北京"}

命名规则

结构体名通常使用驼峰式,首字母大写表示导出:

type Student struct {
Name string // 导出字段
score float64 // 未导出字段
}

结构体内存布局

结构体占用连续内存空间:

type Point struct {
X int
Y int
}

p := Point{X: 10, Y: 20}
fmt.Printf("地址: %p\n", &p)
fmt.Printf("X 地址: %p, Y 地址: %p\n", &p.X, &p.Y)

方法

方法定义

Go 中的方法是与特定类型绑定的函数:

type Rectangle struct {
Width float64
Height float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}

rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area()) // 50

rect.Scale(2)
fmt.Println(rect.Area()) // 200

值接收者 vs 指针接收者

场景推荐使用
方法不需要修改结构体值接收者
方法需要修改结构体指针接收者
结构体较大指针接收者
结构体包含 sync.RWMutex指针接收者

何时使用指针

type Counter struct {
count int
}

// ❌ 值接收者:无法修改
func (c Counter) Increment() {
c.count++
}

// ✅ 指针接收者:可以修改
func (c *Counter) Increment() {
c.count++
}

// 何时使用值接收者
func (c Counter) GetCount() int {
return c.count // 只读取不修改
}

结构体嵌套

匿名嵌套

type Address struct {
City string
State string
}

type Person struct {
Name string
Address // 匿名嵌套
}

p := Person{
Name: "张三",
Address: Address{
City: "北京",
State: "北京",
},
}

// 可以直接访问嵌套字段
fmt.Println(p.City) // 北京
fmt.Println(p.Address.City) // 北京

命名嵌套

type Address struct {
City string
State string
}

type Person struct {
Name string
Home Address // 命名嵌套
Work Address
}

p := Person{
Name: "张三",
Home: Address{City: "北京", State: "北京"},
Work: Address{City: "上海", State: "上海"},
}

多层嵌套

type Country struct {
Name string
}

type Province struct {
Country Country
Name string
}

type City struct {
Province Province
Name string
}

c := City{
Province: Province{
Country: Country{Name: "中国"},
Name: "四川",
},
Name: "成都",
}

fmt.Println(c.Province.Country.Name) // 中国

结构体相等性

可比较的结构体

如果结构体的所有字段都是可比较的,结构体也可以比较:

type Point struct {
X int
Y int
}

p1 := Point{X: 1, Y: 2}
p2 := Point{X: 1, Y: 2}
p3 := Point{X: 2, Y: 1}

fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false

不可比较的结构体

包含切片、映射、函数的结构体不能比较:

type Person struct {
Name string
hobbies []string // 切片,不可比较
}

// ❌ 编译错误
// fmt.Println(p1 == p2)

JSON 序列化

基本序列化

import "encoding/json"

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
City string `json:"city,omitempty"` // 空值省略
}

p := Person{Name: "张三", Age: 25}

// 序列化为 JSON
data, err := json.Marshal(p)
fmt.Println(string(data))
// {"name":"张三","age":25,"city":""}

// 格式化输出
data, _ = json.MarshalIndent(p, "", " ")
fmt.Println(string(data))

反序列化

jsonStr := `{"name":"李四","age":30,"city":"上海"}`

var p Person
err := json.Unmarshal([]byte(jsonStr), &p)
fmt.Println(p.Name, p.Age, p.City) // 李四 30 上海

忽略字段

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
password string `json:"-"` // 忽略此字段
}

构造函数

Go 没有内置构造函数,但可以创建工厂函数:

type Person struct {
Name string
Age int
}

// 构造函数
func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
}
}

// 使用
p := NewPerson("张三", 25)

多构造函数

type Person struct {
Name string
Age int
Email string
}

// 普通构造函数
func NewPerson(name string, age int) *Person {
return &Person{Name: name, Age: age}
}

// 带邮箱的构造函数
func NewPersonWithEmail(name string, age int, email string) *Person {
return &Person{Name: name, Age: age, Email: email}
}

// 默认构造函数
func NewDefaultPerson() *Person {
return &Person{Name: "匿名", Age: 0}
}

组合与继承

组合模式

type Animal struct {
Name string
}

func (a *Animal) Speak() {
fmt.Println("...")
}

type Dog struct {
Animal // 嵌入
Breed string
}

func (d *Dog) Speak() {
fmt.Println("汪汪汪")
}

d := Dog{
Animal: Animal{Name: "旺财"},
Breed: "金毛",
}

d.Speak() // 汪汪汪(自己定义的)
d.Animal.Speak() // ...(嵌入的)

接口组合

type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

// 组合接口
type ReadWriter interface {
Reader
Writer
}

小结

  1. 结构体:自定义复合类型
  2. 方法:与类型绑定的函数
  3. 指针接收者:用于修改结构体或大结构体
  4. 嵌套:匿名嵌套和命名嵌套
  5. JSON:结构体标签控制序列化
  6. 构造函数:工厂函数模式

练习

  1. 定义一个矩形结构体,实现面积和周长计算方法
  2. 创建一个学生结构体,包含姓名、年龄和多门课程成绩
  3. 给学生结构体添加计算平均成绩的方法
  4. 实现一个 Person 结构体的 JSON 序列化
  5. 使用嵌套结构体创建公司组织结构