
💡 核心概念: Go 语言没有 extends 关键字,不支持传统的类继承。Go 使用结构体嵌入(Struct Embedding)来实现代码复用。这是一种“Has-a”(有一个)关系,而不是“Is-a”(是一个)关系,但用起来很像继承。
我们定义一个基础结构体 Person,然后把它“嵌入”到 Maid(侍女)中。注意,我们只写类型名 Person,不写字段名,这叫匿名字段。
package main
import "fmt"
// 基础结构体:人
type Person struct {
Name string
Age int
}
// Person 的方法
func (p *Person) SayHello() {
fmt.Printf("大家好,我是 %s,今年 %d 岁。\n", p.Name, p.Age)
}
// 侍女结构体
type Maid struct {
Person // 👈 核心:直接嵌入 Person 结构体
Skill string
}
func main() {
// 初始化的时候,要指定内部结构体
qiuxiang := Maid{
Person: Person{
Name: "秋香",
Age: 18,
},
Skill: "吟诗作对",
}
// ✨ 神奇之处:直接访问 Person 的字段
fmt.Println(qiuxiang.Name) // 等同于 qiuxiang.Person.Name
// ✨ 神奇之处:直接调用 Person 的方法 (方法提升)
qiuxiang.SayHello()
}

SayHello 方法,但我却能直接用!这就好像我天生就会一样,不用显式地写 qiuxiang.Person.SayHello() 这么麻烦。
如果 Maid 自己也定义了一个 SayHello 方法,会发生什么呢?Go 会优先使用外层的(自己的)方法。这叫方法屏蔽(Shadowing)。
// Maid 自己定义了 SayHello
func (m *Maid) SayHello() {
fmt.Printf("小女子 %s 给公子请安了~ (技能:%s)\n", m.Name, m.Skill)
}
func main() {
qiuxiang := Maid{
Person: Person{Name: "秋香"},
Skill: "点秋香",
}
// 调用的是 Maid 自己的 SayHello
qiuxiang.SayHello()
// 如果非要调用老爸的,只能显式调用
qiuxiang.Person.SayHello()
}

Go 的组合比继承更灵活,因为你可以嵌入多个结构体,就像学会多种武功!
如果 Person 和 KungFu 都有一个叫 Name 的字段,那 monk.Name 到底是谁的?
华夫人: 笨蛋!这时候编译器会晕倒(报错:ambiguous selector)。你必须显式指定 monk.Person.Name 或 monk.KungFu.Name!
type KungFu struct {
Style string
}
func (k *KungFu) Attack() {
fmt.Println("使出了", k.Style)
}
// 护院武僧:既是人,又会功夫
type Monk struct {
Person
KungFu
}
func main() {
monk := Monk{
Person: Person{Name: "夺命书生"},
KungFu: KungFu{Style: "面目全非脚"},
}
monk.SayHello() // 来自 Person
monk.Attack() // 来自 KungFu
}
当结构体嵌入和方法重写同时存在时,请判断下面的代码会输出什么。
package main
import "fmt"
type Master struct {}
func (m *Master) Teach() {
fmt.Println("教你画画")
}
type Student struct {
Master
}
// Student 重写了 Teach
func (s *Student) Teach() {
fmt.Println("教你捉弄人")
}
func main() {
s := Student{}
s.Teach()
}
任务: 最终会输出什么?
答案: "教你捉弄人"
解析: 因为 Student 定义了自己的 Teach 方法,所以它“屏蔽”了内部 Master 的 Teach 方法。这就是 Go 语言中的“重写”机制。