一、什么是结构体嵌入?
结构体嵌入 = 在一个结构体中,直接写另一个结构体类型,不写字段名
Go
type Base struct {
ID int
Name string
}
type User struct {
Base // 嵌入结构体(匿名字段)
Age int
}
等价于(语义上):
Go
type User struct {
Base Base
Age int
}
但用法和能力完全不同(这是重点)。
二、最直观的效果:字段"提升"(Promoted Fields)
1️⃣ 直接访问被嵌入结构体的字段
Go
u := User{
Base: Base{
ID: 1,
Name: "Tom",
},
Age: 18,
}
fmt.Println(u.ID) // 1
fmt.Println(u.Name) // Tom
fmt.Println(u.Base.ID) // 1(也可以)
👉 Base 的字段被"提升"到了 User 上
三、方法也会被"提升"(非常关键)
1️⃣ 嵌入结构体的方法
Go
type Base struct{}
func (b Base) Hello() {
fmt.Println("hello from Base")
}
type User struct {
Base
}
bash
var u User
u.Hello() // 等价于 u.Base.Hello()
📌 字段 + 方法都会被提升
四、结构体嵌入 ≠ 继承,但很像
Go 没有 extends,但结构体嵌入提供了:
| 能力 | Go 结构体嵌入 |
|---|---|
| 代码复用 | ✅ |
| 方法复用 | ✅ |
| 多继承 | ✅(可嵌入多个) |
| 父子类型关系 | ❌(不是 is-a) |
五、嵌入多个结构体(类似多继承)
Go
type Logger struct{}
func (l Logger) Log() { fmt.Println("log") }
type Auth struct{}
func (a Auth) Check() { fmt.Println("auth") }
type Service struct {
Logger
Auth
}
Go
s := Service{}
s.Log()
s.Check()
👉 Go 允许多个嵌入结构体
六、字段 / 方法冲突规则(必考点)
1️⃣ 同名字段冲突
Go
type A struct {
Name string
}
type B struct {
Name string
}
type C struct {
A
B
}
❌ 不能直接:
Go
c.Name // 编译错误:ambiguous selector
✅ 必须显式指定:
Go
c.A.Name
c.B.Name
2️⃣ 子结构体"覆盖"嵌入结构体字段
Go
type Base struct {
Name string
}
type User struct {
Base
Name string
}
Go
u := User{}
u.Name = "UserName" // 访问的是 User.Name
u.Base.Name = "BaseName"
📌 就近原则:优先访问最外层字段
七、值嵌入 vs 指针嵌入(非常重要)
1️⃣ 值嵌入
Go
type User struct {
Base
}
-
会 拷贝一份 Base
-
修改时改的是副本
2️⃣ 指针嵌入(推荐)
Go
type User struct {
*Base
}
Go
b := &Base{ID: 1}
u := User{Base: b}
u.ID = 2
fmt.Println(b.ID) // 2
✅ 共享同一份数据
✅ 常用于组合、组件设计
八、嵌入结构体与接口的"神级配合"
这是 Go 非常核心的设计思想。
1️⃣ 嵌入结构体自动"继承"接口实现
Go
type Speaker interface {
Speak()
}
type Human struct{}
func (h Human) Speak() {
fmt.Println("human speak")
}
type Student struct {
Human
}
Go
var s Speaker = Student{} // ✅ Student 自动实现 Speaker
s.Speak()
📌 只要嵌入的结构体实现了接口,外层结构体也实现了接口
九、嵌入基础类型(不常用但要知道)
Go
type MyInt int
type Counter struct {
MyInt
}
Go
c := Counter{}
c.MyInt = 10
⚠️ 不如结构体嵌入常见,但合法
十、结构体嵌入 vs 普通字段 对比
| 对比项 | 嵌入结构体 | 普通字段 |
|---|---|---|
| 访问方式 | u.ID | u.Base.ID |
| 方法提升 | ✅ | ❌ |
| 接口继承 | ✅ | ❌ |
| 语义 | 组合 | 拥有 |
📌 推荐原则 :
👉 想复用行为(方法) → 用嵌入
👉 只是数据成员 → 用普通字段
十一、工程实践中的典型用法
1️⃣ Web / 业务模型
Go
type Model struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
}
type Order struct {
Model
OrderNo string
}
(GORM 就大量用这个模式)
2️⃣ 装饰 / 组合模式
Go
type Service struct {
*Logger
*DB
}
十二、常见坑总结 ⚠️
-
❌ 以为是继承(不是)
-
❌ 字段冲突没处理
-
❌ 不理解指针嵌入 vs 值嵌入
-
❌ 滥用嵌入(嵌太多层可读性差)