11. 面向对象编程——继承

11. 面向对象编程——继承1 看一个问题 引出继承的必要性 一个小问题 看个学生考试系统的程序 extends01 go 提出代码复用的问题 package main import fmt 编写一个学生考试系统 小学生 type Pupil struct Name string

大家好,我是讯享网,很高兴认识大家。

1. 看一个问题,引出继承的必要性

一个小问题,看个学生考试系统的程序 extends01.go,提出代码复用的问题


讯享网

package main import ( "fmt" ) // 编写一个学生考试系统 // 小学生 type Pupil struct { Name string Age int Score int } // 显示他的成绩 func (p *Pupil) ShowInfo() { fmt.Printf("学生名=%v 年龄=%v 成绩=%v \n",p.Name,p.Age, p.Score) } func (p *Pupil) SetScore(score int) { // 业务判断 p.Score = score } func (p *Pupil) testing() { fmt.Println("小学生正在考试中......") } // 大学生,研究生... type Graduate struct { Name string Age int Score int } func (g *Graduate) ShowInfo() { fmt.Printf("学生名=%v 年龄=%v 成绩=%v \n",g.Name,g.Age, g.Score) } func (g *Graduate) SetScore(score int) { // 业务判断 g.Score = score } func (g *Graduate) testing() { fmt.Println("大学生正在考试中......") } func main() { // 测试 var pupil = Pupil{ Name: "tom", Age: 10, } pupil.testing() pupil.SetScore(90) pupil.ShowInfo() var graduate = &Graduate{ Name: "mary", Age: 20, } graduate.testing() graduate.SetScore(90) graduate.ShowInfo() } 

讯享网

对上面代码的总结:

1)Pupil和Graduate 两个结构体的字段和方法几乎一样,但是我们却写了相同的代码,代码复用性不强

2)出现代码冗余,而且代码不利于维护,同时也不利于功能的扩展。

3)解决方法——通过继承的方式来解决。

2. 继承的基本介绍和示意图

继承可以解决代码的复用性,让我们的编程更加靠近人类的思维。

当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如刚才的Student),在该结构体中定义这些相同的属性和方法。

其它的结构体不需要重新定义这些属性(字段)和方法,只需要嵌套一个Student匿名结构体即可。【画出示意图】

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

3. 嵌套匿名结构体的基本语法

type Goods struct{

        Name string

        Price int

}

type Book struct {

        Goods // 这里就是嵌套匿名结构体Goods

        Writer string

}

4. 快速入门案例

案例

我们对extends01.go进行改进,使用嵌套匿名结构体的方式来实现继承特性,请大家注意体会这样编程的好处

讯享网package main import ( "fmt" ) // 编写一个学生考试系统 type Student struct { Name string Age int Score int } func (stu *Student) ShowInfo() { fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n",stu.Name,stu.Age,stu.Score) } func (stu *Student) SetScore(score int) { // 业务判断 stu.Score = score } // 小学生 type Pupli struct { Student // 嵌入了Student匿名结构体 } // Pupli结构体特有的方法,保留 func (p *Pupli) testing() { fmt.Println("小学生正在考试中......") } // 大学生 type Graduate struct{ Student // 嵌入了Student匿名结构体 } // Graduate结构体特有的方法,保留 func (g *Graduate) testing() { fmt.Println("大学生正在考试中......") } func main() { pupli := &Pupli{} pupli.Student.Name = "tom~" pupli.Student.Age = 8 pupli.testing() pupli.Student.SetScore(80) pupli.Student.ShowInfo() graduate := &Graduate{} graduate.Student.Name = "mary~~" graduate.Student.Age = 28 graduate.testing() graduate.Student.SetScore(90) graduate.Student.ShowInfo() }

5. 继承带来的便利性

1)代码的复用性提高了

2)代码的扩展性和维护性提高了

6. 继承的深入讨论

1)结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或小写的字段、方法,都可以直接使用【举例说明】

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 = "tom" b.A.age = 10 b.A.SayOk() b.A.hello() }

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.Name = "tom" b.age = 10 b.SayOk() b.hello() }

对上面代码的总结

(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 = "jack" b.A.Name = "scott" b.age = 100 b.SayOk() b.A.SayOk() b.hello() // 这个地方应该是空串,就近原则,找的是B的, 上面有b.A.Name,则输出的是scott }

(4)当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】如下:

讯享网package main import ( "fmt" ) // 结构体嵌入两个(多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体的名字,否则编译报错 type A struct { Name string age int } type B struct { Name string Score int } type C struct { A B } func main() { var c C // 如果c 没有Name字段,而A 和 B有Name,这时就必须通过指定匿名结构体名字来区分 // 所以 c.Name就会报编译错误,这个规则对方法也是一样的! // c.Name = "tom" // error c.A.Name = "tom" // true fmt.Println(c) }

5)如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字

package main import ( _"fmt" ) // 如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字 type A struct { Name string Age int } type B struct { a A // 有名结构体,组合关系 } func main() { var b B b.Name = "jack" // error } 

6)嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值

讯享网package main import ( "fmt" ) 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() { tv := TV{ Goods { "电视机001", 5000.99, }, Brand{ "海尔", "山东青岛", }, } tv2 := TV{ Goods { Price: 999.9, Name: "电视机002", }, Brand{ Name: "康佳", Address: "海南", }, } fmt.Println("tv",tv) fmt.Println("tv2",tv2) }

小讯
上一篇 2025-03-31 13:59
下一篇 2025-03-19 18:02

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51509.html