结构体嵌套是 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))
}
| 对比项 | 继承 | 组合 |
|---|---|---|
| 关系 | is-a(是一种) | has-a(有一个) |
| 耦合度 | 高 | 低 |
| 灵活性 | 编译时确定 | 运行时可变 |
| 多重继承 | 不支持(单继承) | 支持多重组合 |
本章学习了 Go 语言的结构体嵌套:
| 知识点 | 说明 |
|---|---|
| 命名嵌套 | 有字段名的嵌套 |
| 匿名嵌套 | 无字段名,字段提升 |
| 字段提升 | 可直接访问内部字段 |
| 方法继承 | 内部方法被继承 |
| 方法重写 | 外部定义同名方法 |