结构体 Struct - Go 复合类型
结构体是 Go 中唯一的复合数据类型,用于将相关数据组合在一起。理解结构体是掌握 Go 面向对象编程的基础。
📌 核心概念
🏗️
结构体
字段集合
type T struct
📝
字段
数据成员
Field Type
🏷️
Tag
元数据
`json:"name"`
🆕
构造函数
New 函数
NewT()
结构体定义
📝 定义和初始化
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
City string
}
func main() {
// 方式 1: 按字段顺序初始化
p1 := Person{"Alice", 30, "Beijing"}
// 方式 2: 按字段名初始化 (推荐)
p2 := Person{
Name: "Bob",
Age: 25,
City: "Shanghai",
}
// 方式 3: 零值初始化
var p3 Person
p3.Name = "Carol"
p3.Age = 28
// 方式 4: 使用 new
p4 := new(Person)
p4.Name = "David"
// 访问字段
fmt.Printf("%s is %d years old\n", p1.Name, p1.Age)
// 指针访问 (自动解引用)
pp := &p1
fmt.Printf("%s lives in %s\n", pp.Name, pp.City)
}
💡 结构体要点
- 值类型: 结构体是值类型,赋值会复制
- 字段导出: 大写开头的字段可被外部访问
- 零值: 未初始化的字段为零值
- 指针接收者: 方法修改字段需用指针
构造函数
📝 New 函数模式
package main
import "fmt"
type Config struct {
Host string
Port int
Debug bool
}
// 构造函数
func NewConfig(host string, port int) *Config {
return &Config{
Host: host,
Port: port,
Debug: false, // 默认值
}
}
// 带选项的构造函数
type ConfigOption func(*Config)
func WithDebug(debug bool) ConfigOption {
return func(c *Config) {
c.Debug = debug
}
}
func NewConfigWithOptions(host string, port int, opts ...ConfigOption) *Config {
cfg := &Config{
Host: host,
Port: port,
}
for _, opt := range opts {
opt(cfg)
}
return cfg
}
func main() {
// 简单构造
cfg1 := NewConfig("localhost", 8080)
// 带选项构造
cfg2 := NewConfigWithOptions("localhost", 8080,
WithDebug(true),
)
fmt.Printf("%+v\n", cfg2)
}
结构体方法
📝 值接收者 vs 指针接收者
package main
import "fmt"
type Counter struct {
count int
}
// 值接收者:不能修改原值
func (c Counter) Value() int {
return c.count
}
// 指针接收者:可以修改原值
func (c *Counter) Inc() {
c.count++
}
// 指针接收者:也可以读取
func (c *Counter) GetValue() int {
return c.count
}
func main() {
c := Counter{}
c.Inc()
c.Inc()
fmt.Println(c.Value()) // 2
fmt.Println(c.GetValue()) // 2
// 值和指针都可以调用方法
(&c).Inc() // 指针调用
c.Inc() // 自动取地址
}
结构体比较
📝 相等性比较
package main
import "fmt"
type Point struct {
X int
Y int
}
func main() {
p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{2, 1}
// 结构体可以直接比较 (所有字段都可比较)
fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false
// 指针比较的是地址
pp1 := &p1
pp2 := &p2
fmt.Println(pp1 == pp2) // false (不同地址)
// 解引用后比较
fmt.Println(*pp1 == *pp2) // true
}
// 不能比较的情况:
// 如果结构体包含 slice、map、function 等不可比较类型
type Bad struct {
Data []int // 包含 slice,不能比较
}
最佳实践
✅ 结构体使用建议
- 字段命名: 使用有意义的名称
- 构造函数: 提供 New 函数简化初始化
- 指针接收者: 修改字段或大结构体用指针
- 字段导出: 只导出必要的字段
- 零值可用: 设计时考虑零值是否有意义