- package main
-
- import (
- "fmt"
- )
-
- //编写一个学生考试系统
- 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(s float64) {
- //逻辑判断
- p.Score = s
- }
- //考试状态
- func (p *Pupil) status() {
- fmt.Println("正在考试")
- }
-
- // 大学生,高中生,...
- func main() {
- p := &Pupil{
- Name: "Jack",
- Age: 11,
- Score: 110.22,
- }
-
- p.status()//正在考试
- p.SetScore(22)
- p.showInfo()//学生姓名:Jack,年龄:11,成绩:22
- }
对上面代码总结:
1.Pupil和Graduate两个结构体字段和方法几乎一样,有很多相同的代码
2.出现代码的冗余,而且代码不利于维护,同时也不利于功能的扩展
3.解决方法:通过继承方式来解决
继承可以解决代码的复用,让编程更加靠近人类思维.
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法.
其他结构体不需要重新定义这些属性(字段)和方法,只需要嵌套一个匿名结构体即可
也就是说:
在go语言中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体中的字段和方法,从而实现继承特性
type Goods struct {
Name string
Proce int
}
type Book struct {
Goods // 这里就是嵌套匿名结构体Goods
Writer string
}
- package main
-
- import (
- "fmt"
- )
-
- //因为小学生,大学生,高中生都有共有的属性和方法,故抽出来放到Student结构体中
- 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(s float64) {
- //逻辑判断
- stu.Score = s
- }
-
- //编写一个学生考试系统
-
- // 小学生,大学生,高中生,...
- //小学生:
- type Pupil struct {
- Student //嵌入了Student匿名的结构体
- }
- //考试状态:小学生结构体特有的方法,保留
- func (p *Pupil) status() {
- fmt.Println("小学生正在考试")
- }
-
- //大学生:
- type Graduate struct {
- Student //嵌入了Student匿名的结构体
- }
- //考试状态:大学生结构体特有的方法,保留
- func (g *Graduate) status() {
- fmt.Println("大学生正在考试")
- }
-
- func main() {
- //当对结构体嵌入了匿名的结构体时,使用的方法会发生变化
- p := &Pupil{}
- p.Student.Name = "jack"
- p.Student.Age = 1
- p.status()//小学生正在考试
- p.Student.SetScore(90.01)
- p.Student.showInfo()//学生姓名:jack,年龄:1,成绩:90.01
-
- g := &Graduate{}
- g.Student.Name = "Mary"
- g.Student.Age = 23
- g.status()//大学生正在考试
- g.Student.SetScore(190.01)
- g.Student.showInfo()//学生姓名:Mary,年龄:23,成绩:190.01
- }

- package main
-
- import (
- "fmt"
- )
-
- //因为小学生,大学生,高中生都有共有的属性和方法,故抽出来放到Student结构体中
- 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(s float64) {
- //逻辑判断
- stu.Score = s
- }
-
- //给Student增加一个方法,那么Pupil,Graduate都可以使用这个方法
- func (stu *Student) GetSum(n1 int, n2 int) int {
- return n1 + n2
- }
-
-
- //编写一个学生考试系统
-
- // 小学生,大学生,高中生,...
- //小学生:
- type Pupil struct {
- Student //嵌入了Student匿名的结构体
- }
- //考试状态:小学生结构体特有的方法,保留
- func (p *Pupil) status() {
- fmt.Println("小学生正在考试")
- }
-
- //大学生:
- type Graduate struct {
- Student //嵌入了Student匿名的结构体
- }
- //考试状态:大学生结构体特有的方法,保留
- func (g *Graduate) status() {
- fmt.Println("大学生正在考试")
- }
-
- func main() {
- //当对结构体嵌入了匿名的结构体时,使用的方法会发生变化
- p := &Pupil{}
- p.Student.Name = "jack"
- p.Student.Age = 1
- p.status()//小学生正在考试
- p.Student.SetScore(90.01)
- p.Student.showInfo()//学生姓名:jack,年龄:1,成绩:90.01
- result := p.Student.GetSum(10, 20)
-
- fmt.Println(result)
- g := &Graduate{}
- g.Student.Name = "Mary"
- g.Student.Age = 23
- g.status()//大学生正在考试
- g.Student.SetScore(190.01)
- g.Student.showInfo()//学生姓名:Mary,年龄:23,成绩:190.01
- result2 := g.Student.GetSum(110, 20)
- fmt.Println(result2)
- }
(1).代码的复用性提高了
(2).代码的扩展性和维护性提高了
- package main
-
- import(
- "fmt"
- )
-
- type A struct {
- Name string
- age int
- }
- func (a *A) SayOk() {
- fmt.Println("A SayOk", a.Name)
- }
- func (a *A) hello() {
- fmt.Println("A hello", a.Name)
- }
-
- type B struct {
- A
- }
- func main() {
- var b B
- b.A.Name = "Mary"
- b.A.age = 10
- b.A.SayOk()//A SayOk Mary
- b.A.hello()//A hello Mary
- }
- package main
-
- import(
- "fmt"
- )
-
- type A struct {
- Name string
- age int
- }
- func (a *A) SayOk() {
- fmt.Println("A SayOk", a.Name)
- }
- func (a *A) hello() {
- fmt.Println("A hello", a.Name)
- }
-
- type B struct {
- A
- }
- func main() {
- var b B
- b.A.Name = "Mary"
- b.A.age = 10
- b.A.SayOk()//A SayOk Mary
- b.A.hello()//A hello Mary
-
- //上面的写法可以简化
- b.Name = "Jack"
- b.age = 20
- b.SayOk()//A SayOk Jack
- b.hello()//A hello Jack
- }
对上面代码的说明:
1).当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name 2).编译器会先看 b 对应的类型有没有 Name ,如果有,则直接调用 B 类型的 Name 字段 3).如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果役有继续查找,如果都找不到就报错
- package main
-
- import(
- "fmt"
- )
-
- type A struct {
- Name string
- age int
- }
- func (a *A) SayOk() {
- fmt.Println("A SayOk", a.Name)
- }
- func (a *A) hello() {
- fmt.Println("A hello", a.Name)
- }
-
- type B struct {
- A
- Name string
- }
-
- func (b *B) SayOk() {
- fmt.Println("B SayOk", b.Name)
- }
- func main() {
- var b B
- b.Name = "Linda" //ok
-
- b.age = 30 //ok
- b.SayOk()//b SayOk Linda
-
- b.hello()//A hello
- b.A.Name = "Tom"
- b.hello()//A hello Tom
- b.A.SayOk()//A SayOk Tom
- }
- package main
-
- import(
- "fmt"
- )
- type A struct{
- Name string
- age int
- }
- type B struct{
- Name string
- score float64
- }
- type C struct{
- A
- B
- //Name string
- }
-
- func main() {
- var c C
- //如果C没有Name字段,而A,B有Name,这时就必须通过指定匿名结构体名字区分
- //c.Name报错,编译错误,这个规则对方法同样适用
- // c.Name = "tom" //error
- // c.A.Name = "tom" //ok
-
- }
- package main
-
- import(
- "fmt"
- )
- type A struct{
- Name string
- age int
- }
- //组合结构体
- type D struct{
- a A//有名结构体
- }
- func main() {
- //如果D中是一个有名结构体,则访问有名结构体的字段时,必须带上有名结构体的名字,如:d.a.Name = "tom"
- var d D
- // d.Name = "Mary"//error: 结构体D中没有Name,里面有一个有名的结构体A
- d.a.Name = "tom" //ok
-
- }
- package main
-
- import(
- "fmt"
- )
- type A struct{
- Name string
- age int
- }
- type B struct{
- Name string
- score float64
- }
- type C struct{
- A
- B
- //Name string
- }
-
- //组合结构体
- type D struct{
- a A//有名结构体
- }
-
-
- type Goods struct{
- Name string
- Price float64
- }
-
- type Brand struct{
- Name string
- Address string
- }
-
- type TV struct {
- Goods
- Brand
- }
-
- type TV2 struct {
- *Goods
- *Brand
- }
-
- func main() {
- // var c C
- //如果C没有Name字段,而A,B有Name,这时就必须通过指定匿名结构体名字区分
- //c.Name报错,编译错误,这个规则对方法同样适用
- // c.Name = "tom" //error
- // c.A.Name = "tom" //ok
-
- //如果D中是一个有名结构体,则访问有名结构体的字段时,必须带上有名结构体的名字,如:d.a.Name = "tom"
- var d D
- // d.Name = "Mary"//error: 结构体D中没有Name,里面有一个有名的结构体A
- d.a.Name = "tom" //ok
-
- //嵌套匿名结构体后,也可在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
- tv := TV{Goods{"电视剧机", 110.22}, Brand{"华硕","四川"},}
- fmt.Println("tv:", tv)
- tv2 := TV{
- Goods{
- Name : "电视剧机",
- Price : 110.22,
- },
- Brand{
- Name : "华硕",
- Address : "四川",
- },
- }
- fmt.Println("tv2:", tv2)
- tv3 := TV2{&Goods{"电视剧机", 110.22}, &Brand{"华硕","四川"},}
- fmt.Println("tv3:", *tv3.Goods, *tv3.Brand)
-
- tv4 := TV2{
- &Goods{
- Name : "电视剧机",
- Price : 110.22,
- },
- &Brand{
- Name : "华硕",
- Address : "四川",
- },
- }
- fmt.Println("tv4:" , *tv4.Goods, *tv4.Brand)
- }
说明:
1).如果一个结构体有int类型的匿名字段,就不能有第二个
2).如果需要有多个int的字段,则必须给int字段指定名字
- package main
-
- import(
- "fmt"
- )
- type Monster struct{
- Name string
- Age int
- }
- type E struct{
- Monster
- int
- n int
- }
-
- func main() {
- //演示匿名字段是基本数据类型
- var e E
- e.Name = "猪八戒"
- e.Age = 12
- e.int = 20
- e. = 30
- fmt.Println("e:",e)
- }
如一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承
- package main
-
- import(
- "fmt"
- )
- type Goods struct{
- Name string
- Price float64
- }
- type Brand struct{
- Name string
- Address string
- }
-
- type TV struct {
- Goods
- Brand
- }
-
- func main() {
- //嵌套匿名结构体后,也可在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
- tv := TV{Goods{"电视剧机", 110.22}, Brand{"华硕","四川"},}
- fmt.Println("tv:", tv)
- tv2 := TV{
- Goods{
- Name : "电视剧机",
- Price : 110.22,
- },
- Brand{
- Name : "华硕",
- Address : "四川",
- },
- }
- fmt.Println("tv2:", tv2)
多重继承细节说明:
1).如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分
2).为了保证代码的简洁性,建议大家尽量不使用多重继承