先看不采用继承的情况
代码
package main
import (
"fmt"
)
type Student struct {
}
// 小学生考试
type Pupil struct {
Name string
Age int
Score float64
}
func (p *Pupil) ShowInfo() {
fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", p.Name, p.Age, p.Score)
}
func (p *Pupil) SetScore(score float64) {
p.Score = score
}
func (p *Pupil) testing() {
fmt.Println("小学生正在考试")
}
// 大学生考试
type Graduate struct {
Name string
Age int
Score float64
}
func (p *Graduate) ShowInfo() {
fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", p.Name, p.Age, p.Score)
}
func (p *Graduate) SetScore(score float64) {
p.Score = score
}
func (p *Graduate) testing() {
fmt.Println("大学生正在考试")
}
func main() {
// 小学生
var pupil = &Pupil{
Name: "tom",
Age: 6,
}
pupil.testing()
pupil.SetScore(99.89)
pupil.ShowInfo()
// 大学生
var graduate = &Graduate{
Name: "bill",
Age: 19,
}
graduate.testing()
graduate.SetScore(99.89)
graduate.ShowInfo()
}
小学生正在考试
名字:tom, 年龄: 6, 分数:99.89
大学生正在考试
名字:bill, 年龄: 19, 分数:99.89
问题引出
- 上面 Pupil 和 Graduate 结构体的字段和方法一样,但是却写了相同的代码
- 代码冗余,不利于维护,不利于扩展
解决办法——继承
继承——解决代码复用
抽象出一个新的共性的结构体 Student(共同的属性和方法),在这个结构体中定义相同的属性和方法
其它结构体不需要重复定义这些属性和方法,只需要嵌套一个 Student 匿名结构体即可。
在Golang中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的属性和方法,从而实现了继承。
用继承(嵌套匿名结构体)实现
代码
package main
import (
"fmt"
)
type Student struct {
Name string
Age int
Score float64
}
func (stu *Student) ShowInfo() {
fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score float64) {
stu.Score = score
}
// 新增方法
func (stu *Student) DoHomework() {
fmt.Println("学生在做作业")
}
// 小学生考试
type Pupil struct {
Student
}
// 保留 Pupil 特有方法
func (p *Pupil) testing() {
fmt.Println("小学生正在考试")
}
// 大学生考试
type Graduate struct {
Student
}
// 保留 Graduate 特有方法
func (p *Graduate) testing() {
fmt.Println("大学生正在考试")
}
func main() {
// 小学生
var pupil = &Pupil{}
pupil.Name = "tom"
pupil.Age = 6
pupil.testing()
pupil.SetScore(99.89)
pupil.ShowInfo()
pupil.DoHomework()
// 大学生
var graduate = &Graduate{}
graduate.Name = "bill"
graduate.Age = 19
graduate.testing()
graduate.SetScore(99.89)
graduate.ShowInfo()
pupil.DoHomework()
}
继承使用细节
- 结构体可以使用嵌套匿名结构体的所有字段和方法(不论标识符是大写还是小写)
- 如果内部类型和外部类型有相同的字段或者方法,调用的时候采用就近原则,如果希望访问和设置匿名结构体的字段和方法,可以通过匿名结构体来区分
- 结构体嵌入了两个(或多个)匿名结构体,如果两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段或方法),调用时,必须指定匿名结构体的名字,否则会报错。
- 如果一个 struct 嵌套了有名结构体,这种模式就是组合,如果时组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字。
- 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
- 多重继承 -> 嵌套多个结构体,同样可以访问字段和方法(为了代码简洁,嵌套关系清晰,建议不要使用多重继承)
package main
import (
"fmt"
)
type A struct {
Name string
Age int
}
type B struct {
Name string
Age int
}
type C struct {
A
B
// Name string
}
type D struct {
a A
}
type Goods struct {
Name string
Price float64
}
type Brand struct {
Name string
addr string
}
// 多重继承
type TV struct {
Goods
Brand
}
type TV2 struct {
*Goods
*Brand
}
type Monster struct {
Name string
Age int
}
type E struct {
Monster
int
}
func main() {
var c C
// 如果C没有Name字段,而A和B有Name, 这时就必须通过指定匿名结构体名字来区分
// c.Name = "tom" // ambiguous selector c.Name 模糊的选择器
c.A.Name = "tom"
fmt.Println("c = ", c)
//嵌入 有名结构体 => 组合
// D 中存在有名结构体,在访问有名结构体的字段或者方法的时候,必须带上有名结构体的名字
var d D
d.a.Name = "xixi"
fmt.Println("d = ", d)
// 字面量形式创建组合实例
d2 := D{
a: A{
Name: "jerry",
Age: 2,
},
}
fmt.Println("d2 = ", d2)
// 创建 TV 实例
tv := TV{Goods{"小米电视", 200}, Brand{"xiaomi", "武汉"}}
fmt.Println("tv = ", tv)
tv02 := TV{
Goods{
Name: "华为电视",
Price: 220,
},
Brand{
Name: "huawei",
addr: "深圳",
},
}
fmt.Println("tv02 = ", tv02)
// 内层结构是地址形式时,创建实例
tv03 := TV2{&Goods{"熊猫电视", 180}, &Brand{"熊猫", "中国"}}
// fmt.Println("tv03 = ", tv03) // {0xc0000040a8 0xc00004e3e0}
fmt.Println("tv03 = ", tv03.Goods, tv03.Brand) // &{熊猫电视 180} &{熊猫 中国}
fmt.Println("tv03 = ", *tv03.Goods, *tv03.Brand) // {熊猫电视 180} {熊猫 中国}
// int 匿名字段是基本数据类型
var e E
e.Name = "牛牛"
e.Age = 3
e.int = 20
fmt.Println("e = ", e) // {{牛牛 3} 20}
}