golang面向对象编程思想-继承
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。其他结构体不需要重新定义这些属性和方法,只需嵌套该结构体的匿名结构体即可。也就是说在Golang中,如果一个struct嵌套了另一个结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性
例子:
编写一个学生考试系统
项目结构图:
student.go
package model
import "fmt"
type Student struct {
Name string
Age int
Score float32
}
func (stu *Student) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score float32) {
stu.Score = score
}
pupil.go
package model
import "fmt"
// 小学生
type Pupil struct {
Student // 嵌入Student匿名结构体,实现继承
}
func (pupil Pupil) Testing() {
fmt.Println("小学生正在考试中...")
}
graduate.go
package model
import "fmt"
// 大学生
type Graduate struct {
Student // 嵌入Student匿名结构体,实现继承
}
func (graduate Graduate) Testing() {
fmt.Println("大学生正在考试中...")
}
main.go
package main
import (
"model"
)
func main() {
pupil := model.Pupil{}
pupil.Name = "tom"
pupil.Age = 10
pupil.Testing()
pupil.SetScore(80)
pupil.ShowInfo()
graduate := model.Graduate{}
graduate.Name = "mary"
graduate.Age = 20
graduate.Testing()
graduate.SetScore(100)
graduate.ShowInfo()
}
输出结果:
小学生正在考试中...
学生名=tom 年龄=10 成绩=80
大学生正在考试中...
学生名=mary 年龄=20 成绩=100
1.细节
- 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。
package main
import (
"fmt"
)
type A struct {
Name string
}
func (a *A) SayHello() {
fmt.Println("A.......SayHello", a.Name)
}
type B struct {
A
Name string
}
func (b *B) SayHello() {
fmt.Println("B.......SayHello", b.Name)
}
func main() {
b := B{}
b.Name = "tom"
b.SayHello()
b.A.SayHello()
b.A.Name = "marry"
b.A.SayHello()
}
输出结果:
B.......SayHello tom
A.......SayHello
A.......SayHello marry
- 结构体嵌入两个(或多个)匿名结构体,若两个结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错
package main
import (
"fmt"
)
type A struct {
Name string
Age int
}
type B struct {
Name string
Score float32
}
func (a *A) SayHello() {
fmt.Println("A.......SayHello", a.Name)
}
func (b *B) SayHello() {
fmt.Println("B.......SayHello", b.Name)
}
type C struct {
A
B
}
func main() {
var c C
// 如果C没有Name字段,而A和B有Name字段,这时就必须通过指定匿名结构体名字区分
// 所以c.Name就会编译错误,这个规则对方法也是一样的
// c.Name = "tom" // 编译报错
c.A.Name = "tom"
c.Age = 10
c.B.Name = "jack"
c.Score = 88.8
// c.SayHello() // 编译报错
fmt.Println("c=", c)
c.A.SayHello()
c.B.SayHello()
}
输出结果:
c= {{tom 10} {jack 88.8}}
A.......SayHello tom
B.......SayHello jack
- 如果一个结构体嵌套了一个有名结构体,这种模式是组合,如果是组合关系,那么在访问组合结构体的字段或方法时,必须带上结构体名字的
package main
import (
"fmt"
)
type A struct {
Name string
Age int
}
type B struct {
a A // 组合关系
}
func (a *A) SayHello() {
fmt.Println("A.......SayHello", a.Name)
}
func main() {
var b B
// 如果一个结构体嵌套了一个有名结构体,这种模式是组合,如果是组合关系,
// 那么在访问组合结构体的字段或方法时,必须带上结构体名字的
b.a.Name = "tom"
b.a.Age = 10
fmt.Println("b=", b)
b.a.SayHello()
}
输出结果:
b= {{tom 10}}
A.......SayHello tom
- 嵌套匿名结构体后,也可以在创建结构体变量(实例时),直接指定各个匿名结构体字段的值
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() {
tv1 := TV{Goods{"电视机001", 6000.89}, Brand{"海尔", "山东"}}
// 嵌套匿名结构体后,也可以在创建结构体变量(实例时),直接指定各个匿名结构体字段的值
tv2 := TV{
Goods{
Name: "电视机002",
Price: 8000.89,
},
Brand{
Name: "康佳",
Address: "青岛",
},
}
fmt.Println("tv1=", tv1)
fmt.Println("tv2=", tv2)
}
输出结果:
tv1= {{电视机001 6000.89} {海尔 山东}}
tv2= {{电视机002 8000.89} {康佳 青岛}}
2. 结构体匿名字段是基本数据类型
package main
import (
"fmt"
)
type Goods struct {
Name string
Price float64
}
type TV struct {
Goods
int // 匿名字段是基本数据类型
n int
}
func main() {
var tv TV
tv.Goods = Goods{
Name: "电视机002",
Price: 8000.89,
}
tv.int = 20
tv.n = 30
fmt.Println("tv=", tv)
}
输出结果
tv= {{电视机002 8000.89} 20 30}
说明:如果一个结构体有int类型的匿名字段,就不能有第二个,如果要多个int的字段,就必须给int字段指定名字
3. 多重继承
如一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现多重继承,为了保证代码的简洁性,建议大家尽量不使用多重继承
package main
import (
"fmt"
)
type Goods struct {
Name string
Price float64
}
func (goods Goods) GoodsMethd() {
fmt.Println("GoodsMethd()...")
}
type Brand struct {
Name string
Address string
}
func (brand Brand) BrandMethd() {
fmt.Println("BrandMethd()...")
}
type TV struct {
Goods
Brand
}
func main() {
tv := TV{
Goods{
Name: "电视机002",
Price: 8000.89,
},
Brand{
Name: "康佳",
Address: "青岛",
},
}
fmt.Println("Address=", tv.Address)
fmt.Println("Price=", tv.Price)
tv.GoodsMethd()
tv.BrandMethd()
}
输出结果:
Address= 青岛
Price= 8000.89
GoodsMethd()...
BrandMethd()...