结构体嵌套

结构体嵌套是 Go 语言实现代码复用的核心机制。本章将详细介绍结构体嵌套的使用方法。

什么是结构体嵌套?

结构体嵌套是指在一个结构体中嵌入另一个结构体,实现"组合"。

结构体嵌套示意:

┌─────────────────────────────────────┐
│           Manager                   │
├─────────────────────────────────────┤
│  Employee (嵌入)                    │
│    ├── id: int                      │
│    ├── name: string                 │
│    └── salary: float64              │
│  teamSize: int                      │
└─────────────────────────────────────┘

基本嵌套

命名嵌套

package main

import "fmt"

type Address struct {
    City    string
    Street  string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

func main() {
    p := Person{
        Name: "张三",
        Age:  25,
        Address: Address{
            City:   "北京",
            Street: "长安街",
        },
    }

    fmt.Printf("姓名: %s\n", p.Name)
    fmt.Printf("城市: %s\n", p.Address.City)
}

匿名嵌套(嵌入)

package main

import "fmt"

type Address struct {
    City   string
    Street string
}

type Person struct {
    Name string
    Age  int
    Address
}

func main() {
    p := Person{
        Name: "张三",
        Age:  25,
        Address: Address{
            City:   "北京",
            Street: "长安街",
        },
    }

    fmt.Printf("姓名: %s\n", p.Name)
    fmt.Printf("城市: %s\n", p.City)
    fmt.Printf("街道: %s\n", p.Street)
}

匿名嵌套时,外部结构体可以直接访问内部结构体的字段(字段提升)。

字段提升

package main

import "fmt"

type Inner struct {
    X int
}

type Outer struct {
    Inner
    Y int
}

func main() {
    o := Outer{
        Inner: Inner{X: 10},
        Y:     20,
    }

    fmt.Printf("o.X = %d\n", o.X)
    fmt.Printf("o.Inner.X = %d\n", o.Inner.X)
}

方法继承

嵌入的结构体的方法也会被继承:

package main

import "fmt"

type Animal struct {
    Name string
}

func (a Animal) Speak() {
    fmt.Printf("%s 发出声音\n", a.Name)
}

func (a Animal) Move() {
    fmt.Printf("%s 移动\n", a.Name)
}

type Dog struct {
    Animal
    Breed string
}

func main() {
    dog := Dog{
        Animal: Animal{Name: "旺财"},
        Breed:  "金毛",
    }

    dog.Speak()
    dog.Move()
}

方法重写

package main

import "fmt"

type Animal struct {
    Name string
}

func (a Animal) Speak() {
    fmt.Printf("%s 发出声音\n", a.Name)
}

type Dog struct {
    Animal
}

func (d Dog) Speak() {
    fmt.Printf("%s 汪汪叫\n", d.Name)
}

func main() {
    dog := Dog{Animal: Animal{Name: "旺财"}}

    dog.Speak()
    dog.Animal.Speak()
}

多重嵌套

package main

import "fmt"

type A struct {
    X int
}

type B struct {
    Y int
}

type C struct {
    A
    B
    Z int
}

func main() {
    c := C{
        A: A{X: 1},
        B: B{Y: 2},
        Z: 3,
    }

    fmt.Printf("c.X = %d\n", c.X)
    fmt.Printf("c.Y = %d\n", c.Y)
    fmt.Printf("c.Z = %d\n", c.Z)
}

字段冲突

当多个嵌入结构体有相同字段时,需要显式指定:

package main

import "fmt"

type A struct {
    Name string
}

type B struct {
    Name string
}

type C struct {
    A
    B
}

func main() {
    c := C{
        A: A{Name: "A的Name"},
        B: B{Name: "B的Name"},
    }

    fmt.Printf("c.A.Name = %s\n", c.A.Name)
    fmt.Printf("c.B.Name = %s\n", c.B.Name)
}

实际案例

案例:员工管理系统

package main

import "fmt"

type Employee struct {
    ID     int
    Name   string
    Salary float64
}

func (e Employee) GetInfo() string {
    return fmt.Sprintf("ID: %d, 姓名: %s, 薪资: %.2f", e.ID, e.Name, e.Salary)
}

func (e Employee) CalculateBonus() float64 {
    return e.Salary * 0.1
}

type Manager struct {
    Employee
    TeamSize int
}

func (m Manager) CalculateBonus() float64 {
    return m.Salary*0.2 + float64(m.TeamSize)*1000
}

func (m Manager) GetInfo() string {
    return fmt.Sprintf("%s, 团队人数: %d", m.Employee.GetInfo(), m.TeamSize)
}

func main() {
    emp := Employee{
        ID:     1,
        Name:   "张三",
        Salary: 10000,
    }
    fmt.Println(emp.GetInfo())
    fmt.Printf("奖金: %.2f\n", emp.CalculateBonus())

    fmt.Println()

    mgr := Manager{
        Employee: Employee{
            ID:     2,
            Name:   "李四",
            Salary: 20000,
        },
        TeamSize: 5,
    }
    fmt.Println(mgr.GetInfo())
    fmt.Printf("奖金: %.2f\n", mgr.CalculateBonus())
}

案例:图形组合

package main

import (
    "fmt"
    "math"
)

type Point struct {
    X, Y float64
}

func (p Point) DistanceTo(other Point) float64 {
    dx := p.X - other.X
    dy := p.Y - other.Y
    return math.Sqrt(dx*dx + dy*dy)
}

type Circle struct {
    Center Point
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    TopLeft     Point
    BottomRight Point
}

func (r Rectangle) Width() float64 {
    return r.BottomRight.X - r.TopLeft.X
}

func (r Rectangle) Height() float64 {
    return r.BottomRight.Y - r.TopLeft.Y
}

func (r Rectangle) Area() float64 {
    return r.Width() * r.Height()
}

func main() {
    circle := Circle{
        Center: Point{X: 0, Y: 0},
        Radius: 5,
    }
    fmt.Printf("圆形面积: %.2f\n", circle.Area())

    rect := Rectangle{
        TopLeft:     Point{X: 0, Y: 0},
        BottomRight: Point{X: 10, Y: 5},
    }
    fmt.Printf("矩形面积: %.2f\n", rect.Area())

    p1 := Point{X: 0, Y: 0}
    p2 := Point{X: 3, Y: 4}
    fmt.Printf("两点距离: %.2f\n", p1.DistanceTo(p2))
}

组合 vs 继承

对比项继承组合
关系is-a(是一种)has-a(有一个)
耦合度
灵活性编译时确定运行时可变
多重继承不支持(单继承)支持多重组合

总结

本章学习了 Go 语言的结构体嵌套:

知识点说明
命名嵌套有字段名的嵌套
匿名嵌套无字段名,字段提升
字段提升可直接访问内部字段
方法继承内部方法被继承
方法重写外部定义同名方法