结构体与方法

定义与初始化

结构体将多个字段聚合为一个自定义类型,是 Go 组织数据的基本单元:

package main

import "fmt"

type User struct {
    ID    int64
    Name  string
    Email string
    admin bool // 小写字段:包外不可见
}

func main() {
    // 1. 按字段名初始化(推荐)
    u1 := User{ID: 1, Name: "Alice"}

    // 2. 按位置初始化(不推荐:字段变更时易出错)
    u2 := User{2, "Bob", "bob@example.com", false}

    // 3. 取结构体指针
    u3 := &User{ID: 3, Name: "Carol"}

    fmt.Println(u1, u2, *u3)
}

访问字段统一使用点号语法,Go 会自动在值与指针之间做适配:u3.Name 等价于 (*u3).Name

方法

方法即带有接收者的函数。接收者分为值接收者与指针接收者:

package main

import "fmt"

type Rectangle struct {
    Width, Height float64
}

// 值接收者:方法获得副本,不会修改原结构体
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者:可修改原结构体,且避免大对象拷贝
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 3, Height: 4}
    fmt.Println(rect.Area()) // 12

    rect.Scale(2)
    fmt.Println(rect.Width) // 6(指针接收者修改生效)
}
Tip

选择接收者的经验法则:

  • 方法需要修改接收者,或结构体较大时,使用指针接收者
  • 只读小结构体(如 time.Time)可使用值接收者。
  • 同一类型的所有方法应保持接收者类型一致,不要混用。 :::

嵌入:组合优于继承

Go 没有继承,通过嵌入(embedding)将一个类型复用进另一个类型。被嵌入类型的字段与方法会被"提升"到外层类型:

package main

import "fmt"

type Animal struct {
    Name string
}

func (a Animal) Describe() string {
    return "I am " + a.Name
}

type Dog struct {
    Animal // 嵌入:Dog 获得 Animal 的字段与方法
    Breed  string
}

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    fmt.Println(d.Name)      // 字段提升:等价于 d.Animal.Name
    fmt.Println(d.Describe()) // 方法提升
}

:::warning 嵌入不是继承:d.Describe() 内部访问的仍是 Animal 的字段,不会感知 Dog 自身的扩展。外层方法与内层方法同名时,外层"遮蔽"内层,而非重写覆盖。

结构体标签与反射

字段可附带标签(tag),供 encoding/json、ORM 等运行时通过反射读取:

type Product struct {
    ID    int64  `json:"id"`
    Title string `json:"title"`
    Price int    `json:"price,omitempty"` // 零值时不输出
}

标签的详细应用见 JSON 处理章节。

小结

  • 结构体是值类型,推荐按字段名初始化。
  • 指针接收者可修改原值并避免拷贝;接收者类型应保持一致。
  • 嵌入实现组合复用,方法提升简化调用,但并非继承。