先看不采用继承的情况

小学生考试,大学生考试

代码

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Student struct {
  6. }
  7. // 小学生考试
  8. type Pupil struct {
  9. Name string
  10. Age int
  11. Score float64
  12. }
  13. func (p *Pupil) ShowInfo() {
  14. fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", p.Name, p.Age, p.Score)
  15. }
  16. func (p *Pupil) SetScore(score float64) {
  17. p.Score = score
  18. }
  19. func (p *Pupil) testing() {
  20. fmt.Println("小学生正在考试")
  21. }
  22. // 大学生考试
  23. type Graduate struct {
  24. Name string
  25. Age int
  26. Score float64
  27. }
  28. func (p *Graduate) ShowInfo() {
  29. fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", p.Name, p.Age, p.Score)
  30. }
  31. func (p *Graduate) SetScore(score float64) {
  32. p.Score = score
  33. }
  34. func (p *Graduate) testing() {
  35. fmt.Println("大学生正在考试")
  36. }
  37. func main() {
  38. // 小学生
  39. var pupil = &Pupil{
  40. Name: "tom",
  41. Age: 6,
  42. }
  43. pupil.testing()
  44. pupil.SetScore(99.89)
  45. pupil.ShowInfo()
  46. // 大学生
  47. var graduate = &Graduate{
  48. Name: "bill",
  49. Age: 19,
  50. }
  51. graduate.testing()
  52. graduate.SetScore(99.89)
  53. graduate.ShowInfo()
  54. }
  55. 小学生正在考试
  56. 名字:tom, 年龄: 6, 分数:99.89
  57. 大学生正在考试
  58. 名字:bill, 年龄: 19, 分数:99.89

问题引出

  1. 上面 Pupil 和 Graduate 结构体的字段和方法一样,但是却写了相同的代码
  2. 代码冗余,不利于维护,不利于扩展

    解决办法——继承

    继承——解决代码复用
    抽象出一个新的共性的结构体 Student(共同的属性和方法),在这个结构体中定义相同的属性和方法
    其它结构体不需要重复定义这些属性和方法,只需要嵌套一个 Student 匿名结构体即可。

在Golang中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的属性和方法,从而实现了继承。

用继承(嵌套匿名结构体)实现

高复用,可扩展

代码

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Student struct {
  6. Name string
  7. Age int
  8. Score float64
  9. }
  10. func (stu *Student) ShowInfo() {
  11. fmt.Printf("名字:%v, 年龄: %v, 分数:%v \n", stu.Name, stu.Age, stu.Score)
  12. }
  13. func (stu *Student) SetScore(score float64) {
  14. stu.Score = score
  15. }
  16. // 新增方法
  17. func (stu *Student) DoHomework() {
  18. fmt.Println("学生在做作业")
  19. }
  20. // 小学生考试
  21. type Pupil struct {
  22. Student
  23. }
  24. // 保留 Pupil 特有方法
  25. func (p *Pupil) testing() {
  26. fmt.Println("小学生正在考试")
  27. }
  28. // 大学生考试
  29. type Graduate struct {
  30. Student
  31. }
  32. // 保留 Graduate 特有方法
  33. func (p *Graduate) testing() {
  34. fmt.Println("大学生正在考试")
  35. }
  36. func main() {
  37. // 小学生
  38. var pupil = &Pupil{}
  39. pupil.Name = "tom"
  40. pupil.Age = 6
  41. pupil.testing()
  42. pupil.SetScore(99.89)
  43. pupil.ShowInfo()
  44. pupil.DoHomework()
  45. // 大学生
  46. var graduate = &Graduate{}
  47. graduate.Name = "bill"
  48. graduate.Age = 19
  49. graduate.testing()
  50. graduate.SetScore(99.89)
  51. graduate.ShowInfo()
  52. pupil.DoHomework()
  53. }

继承使用细节

  1. 结构体可以使用嵌套匿名结构体的所有字段和方法(不论标识符是大写还是小写)
  2. 如果内部类型和外部类型有相同的字段或者方法,调用的时候采用就近原则,如果希望访问和设置匿名结构体的字段和方法,可以通过匿名结构体来区分
  3. 结构体嵌入了两个(或多个)匿名结构体,如果两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段或方法),调用时,必须指定匿名结构体的名字,否则会报错。
  4. 如果一个 struct 嵌套了有名结构体,这种模式就是组合,如果时组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字。
  5. 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
  6. 多重继承 -> 嵌套多个结构体,同样可以访问字段和方法(为了代码简洁,嵌套关系清晰,建议不要使用多重继承)
  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type A struct {
  6. Name string
  7. Age int
  8. }
  9. type B struct {
  10. Name string
  11. Age int
  12. }
  13. type C struct {
  14. A
  15. B
  16. // Name string
  17. }
  18. type D struct {
  19. a A
  20. }
  21. type Goods struct {
  22. Name string
  23. Price float64
  24. }
  25. type Brand struct {
  26. Name string
  27. addr string
  28. }
  29. // 多重继承
  30. type TV struct {
  31. Goods
  32. Brand
  33. }
  34. type TV2 struct {
  35. *Goods
  36. *Brand
  37. }
  38. type Monster struct {
  39. Name string
  40. Age int
  41. }
  42. type E struct {
  43. Monster
  44. int
  45. }
  46. func main() {
  47. var c C
  48. // 如果C没有Name字段,而A和B有Name, 这时就必须通过指定匿名结构体名字来区分
  49. // c.Name = "tom" // ambiguous selector c.Name 模糊的选择器
  50. c.A.Name = "tom"
  51. fmt.Println("c = ", c)
  52. //嵌入 有名结构体 => 组合
  53. // D 中存在有名结构体,在访问有名结构体的字段或者方法的时候,必须带上有名结构体的名字
  54. var d D
  55. d.a.Name = "xixi"
  56. fmt.Println("d = ", d)
  57. // 字面量形式创建组合实例
  58. d2 := D{
  59. a: A{
  60. Name: "jerry",
  61. Age: 2,
  62. },
  63. }
  64. fmt.Println("d2 = ", d2)
  65. // 创建 TV 实例
  66. tv := TV{Goods{"小米电视", 200}, Brand{"xiaomi", "武汉"}}
  67. fmt.Println("tv = ", tv)
  68. tv02 := TV{
  69. Goods{
  70. Name: "华为电视",
  71. Price: 220,
  72. },
  73. Brand{
  74. Name: "huawei",
  75. addr: "深圳",
  76. },
  77. }
  78. fmt.Println("tv02 = ", tv02)
  79. // 内层结构是地址形式时,创建实例
  80. tv03 := TV2{&Goods{"熊猫电视", 180}, &Brand{"熊猫", "中国"}}
  81. // fmt.Println("tv03 = ", tv03) // {0xc0000040a8 0xc00004e3e0}
  82. fmt.Println("tv03 = ", tv03.Goods, tv03.Brand) // &{熊猫电视 180} &{熊猫 中国}
  83. fmt.Println("tv03 = ", *tv03.Goods, *tv03.Brand) // {熊猫电视 180} {熊猫 中国}
  84. // int 匿名字段是基本数据类型
  85. var e E
  86. e.Name = "牛牛"
  87. e.Age = 3
  88. e.int = 20
  89. fmt.Println("e = ", e) // {{牛牛 3} 20}
  90. }