Skip to content

结构体

结构体是Go语言中用于组合不同类型数据的自定义类型。

定义结构体

基本定义

go
type Person struct {
    Name string
    Age  int
    City string
}

创建结构体实例

go
package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    // 按顺序初始化
    person1 := Person{"张三", 25, "北京"}
    fmt.Println(person1)
    
    // 使用字段名初始化(推荐)
    person2 := Person{
        Name: "李四",
        Age:  30,
        City: "上海",
    }
    fmt.Println(person2)
    
    // 部分初始化
    person3 := Person{Name: "王五"}
    fmt.Println(person3)  // {王五 0 }
    
    // 零值创建
    var person4 Person
    fmt.Println(person4)  // { 0 }
    
    // 使用new创建指针
    person5 := new(Person)
    fmt.Println(person5)  // &{ 0 }
}

访问和修改字段

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

// 访问字段
fmt.Println(person.Name)  // 张三
fmt.Println(person.Age)   // 25

// 修改字段
person.Age = 26
fmt.Println(person.Age)   // 26

// 通过指针访问
ptr := &person
fmt.Println(ptr.Name)     // 张三(自动解引用)
ptr.Age = 27
fmt.Println(person.Age)   // 27

结构体方法

值接收者方法

go
type Rectangle struct {
    Width  float64
    Height float64
}

// 方法定义
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    
    fmt.Printf("面积: %.2f\n", rect.Area())
    fmt.Printf("周长: %.2f\n", rect.Perimeter())
}

指针接收者方法

go
type Rectangle struct {
    Width  float64
    Height float64
}

// 指针接收者方法(可以修改原值)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    
    rect.Scale(2)
    fmt.Printf("缩放后的宽度: %.2f, 高度: %.2f\n", rect.Width, rect.Height)
    // 输出: 缩放后的宽度: 20.00, 高度: 10.00
}

值接收者 vs 指针接收者

特性值接收者指针接收者
修改原值
性能小结构体适合大结构体适合
一致性-推荐(同一类型的方法保持一致)

结构体嵌套

匿名字段

go
type Address struct {
    City    string
    Country string
}

type Person struct {
    Name    string
    Age     int
    Address  // 匿名字段(嵌入)
}

func main() {
    p := Person{
        Name: "张三",
        Age:  25,
        Address: Address{
            City:    "北京",
            Country: "中国",
        },
    }
    
    // 直接访问嵌入字段
    fmt.Println(p.City)     // 北京
    fmt.Println(p.Country)  // 中国
}

字段提升

go
type Inner struct {
    X int
}

type Outer struct {
    Inner
    X int
}

func main() {
    o := Outer{
        Inner: Inner{X: 10},
        X:     20,
    }
    
    fmt.Println(o.X)       // 20 (外层字段)
    fmt.Println(o.Inner.X) // 10 (内层字段)
}

结构体标签

结构体标签用于为字段添加元数据:

go
type User struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age" validate:"min=0,max=150"`
}

func main() {
    u := User{Name: "张三", Age: 25}
    
    // 使用json标签序列化
    data, _ := json.Marshal(u)
    fmt.Println(string(data))  // {"name":"张三","age":25}
}

构造函数

Go没有构造函数,通常使用工厂函数:

go
type Person struct {
    Name string
    Age  int
}

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

func main() {
    p := NewPerson("张三", 25)
    fmt.Println(p)
}

结构体比较

go
type Point struct {
    X, Y int
}

func main() {
    p1 := Point{1, 2}
    p2 := Point{1, 2}
    p3 := Point{2, 3}
    
    fmt.Println(p1 == p2)  // true
    fmt.Println(p1 == p3)  // false
}

注意

如果结构体包含不可比较的字段(如切片、Map),则结构体本身也不可比较。

结构体方法集

go
type MyInt int

func (m MyInt) ValueMethod() {
    fmt.Println("值方法")
}

func (m *MyInt) PointerMethod() {
    fmt.Println("指针方法")
}

func main() {
    var v MyInt
    var p *MyInt = &v
    
    // 值变量可以调用值方法
    v.ValueMethod()
    
    // 值变量也可以调用指针方法(自动取地址)
    v.PointerMethod()
    
    // 指针变量可以调用所有方法
    p.ValueMethod()
    p.PointerMethod()
}

实用示例

链表节点

go
type Node struct {
    Value int
    Next  *Node
}

func main() {
    // 创建链表: 1 -> 2 -> 3
    n3 := &Node{Value: 3}
    n2 := &Node{Value: 2, Next: n3}
    n1 := &Node{Value: 1, Next: n2}
    
    // 遍历链表
    for node := n1; node != nil; node = node.Next {
        fmt.Println(node.Value)
    }
}

二叉树节点

go
type TreeNode struct {
    Value int
    Left  *TreeNode
    Right *TreeNode
}

func (t *TreeNode) Insert(value int) {
    if value < t.Value {
        if t.Left == nil {
            t.Left = &TreeNode{Value: value}
        } else {
            t.Left.Insert(value)
        }
    } else {
        if t.Right == nil {
            t.Right = &TreeNode{Value: value}
        } else {
            t.Right.Insert(value)
        }
    }
}