golang面向对象编程思想-继承

当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。其他结构体不需要重新定义这些属性和方法,只需嵌套该结构体的匿名结构体即可。也就是说在Golang中,如果一个struct嵌套了另一个结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性
例子:
编写一个学生考试系统
项目结构图:
image
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()...
posted @ 2021-05-31 22:37  若雨蚂蚱  阅读(58)  评论(0编辑  收藏  举报