重要
Go 没有继承,用结构体嵌套(组合)+ 接口实现代码复用。匿名字段会触发字段和方法提升——被嵌套结构体的字段和方法自动提升为外部结构体的一部分。
1. 组合 vs 继承
| Go 组合 | 传统 OOP 继承 |
|---|
| 关系 | has-a | is-a |
| 方式 | 结构体嵌套 | extends 关键字 |
| 多态 | 接口(鸭子类型) | 父类引用指向子类对象 |
| 访问控制 | 首字母大小写 | public/protected/private |
| 多级层级 | 扁平组合,无层级 | 多层继承链 |
Go 的设计哲学是组合优于继承(composition over inheritance)。没有父子类概念,只有组合关系。
2. 匿名字段与字段提升
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| type Person struct {
Name string
Age int
}
type Employee struct {
Person // 匿名字段——Person 的所有字段提升为 Employee 的字段
Company string
}
e := Employee{
Person: Person{Name: "Alice", Age: 30},
Company: "ACME",
}
fmt.Println(e.Name) // "Alice"(字段提升,无需 e.Person.Name)
fmt.Println(e.Age) // 30
|
提升规则:
| 提升内容 | 说明 |
|---|
| 字段 | 被嵌套结构体的所有导出和非导出字段 |
| 方法 | 被嵌套结构体的所有方法 |
| 冲突 | 同名时外部优先(覆盖);多个内部同名 → 编译错误 |
3. 方法覆盖(非重写)
如果外部结构体定义了与内部同名的方法,外部方法会覆盖内部方法。但无法通过外部结构体直接调用被覆盖的内部方法。
1
2
3
4
5
6
7
8
9
10
11
| func (p Person) Greet() string {
return "Hi, I'm " + p.Name
}
func (e Employee) Greet() string {
// 覆盖 Person.Greet()
return e.Person.Greet() + " from " + e.Company // 通过内部字段名显式调用
}
e.Greet() // "Hi, I'm Alice from ACME"
e.Person.Greet() // "Hi, I'm Alice"(显式调用内部方法)
|
不同于 OOP 的 super——Go 中没有隐式的父级调用语法,必须通过内部字段名显式引用。
4. 接口与多态
Go 通过接口实现多态。任何类型只要实现了接口的所有方法,就自动满足该接口(鸭子类型)。
1
2
3
4
5
6
7
8
9
10
11
12
13
| type Greeter interface {
Greet() string
}
func SayHello(g Greeter) {
fmt.Println(g.Greet())
}
p := Person{Name: "Bob"}
e := Employee{Person: Person{Name: "Alice"}, Company: "ACME"}
SayHello(p) // Bob 实现 Greeter
SayHello(e) // Employee 也实现 Greeter
|
| 传统 OOP | Go |
|---|
显式声明 implements | 隐式满足——实现所有方法即可 |
| 类型间有继承层级 | 类型间无层级关系 |
| 编译时检查父子关系 | 编译时检查方法签名匹配 |
5. 总结
- Go 无继承——用结构体嵌套(组合)+ 接口实现代码复用;
- 匿名字段触发字段/方法提升,同名时外部覆盖内部;
- 多态通过接口隐式满足实现,无需显式声明;
- 无需
super 语法——通过内部字段名显式调用被覆盖的方法。
6. 参考