← defer 延迟 | 嵌套与嵌入 →

结构体 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 函数简化初始化
  • 指针接收者: 修改字段或大结构体用指针
  • 字段导出: 只导出必要的字段
  • 零值可用: 设计时考虑零值是否有意义