Go语言面向对象

Go语言面向对象及方法

Go不是面向对象语言,但是却可以借助结构体模拟面向对象的特点。结构体在Go语言中的地位等同其他语言中的class。
GO中这样的设计极大地降低了耦合,包括后面所说的接口,基本都是非侵入式的。

一、面向对象

面向对象的基本特征是:继承、封装、多态

Go中的结构体是一种组合式的结构,对于属性的继承可以使用匿名字段的方式。而封装不必多说,对于多态的支持稍后详述。

结构体的匿名字段

结构体中的字段如果没有名字,称为匿名字段。原理是字段名和类型一样的话可以省略不写。

func main()  {

	a := animal{"Dog", "3", "汪汪"}
	fmt.Println(a)
	fmt.Printf("匿名字段的%s",a.string)
}

type animal struct {
	name string
	age string
	//匿名字段
	string
}


..........................................
//{Dog 3 汪汪}
//匿名字段的汪汪

我们增加一个匿名字段,类型是一个结构体。也就是结构体嵌套了。之前说过访问嵌套的结构体的字段,可以使用

.连续访问。如果嵌套的结构体是匿名的,那么可以直接访问其字段。

func main()  {

	a := animal{"Dog", "3", "汪汪",Dog{"旺财","female"}}
	fmt.Println(a)
	fmt.Printf("匿名字段的%s\n",a.string)
	fmt.Printf("匿名字段的%s\n",a.Dog)
	fmt.Printf("匿名字段的%s\n",a.Dog.sex)
	//省略匿名字段名
	fmt.Printf("匿名字段的%s\n",a.sex)
	fmt.Printf("匿名字段的%s\n",a.Dog.name)
	//名字冲突,指向父结构体
	fmt.Printf("匿名字段的%s\n",a.name)

}

type animal struct {
	name string
	age string
	//匿名字段
	string
	Dog
}

type Dog struct {
	name string
	sex string
}

{Dog 3 汪汪 {旺财 female}}
匿名字段的汪汪
匿名字段的{旺财 female}
匿名字段的female
匿名字段的female
匿名字段的旺财
匿名字段的Dog

继承-(提升字段)

上面的Dog匿名字段变成了提升字段,a.Dog.sex和a.sex都可以访问,这就是模拟的继承。

再写一个Demo:

func main()  {
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	//提升字段
	fmt.Printf("学校%s老师的年龄是%d\n",school.Teacher.teaName,school.Teacher.teaAge)
	fmt.Printf("学校%s老师的年龄是%d\n",school.teaName,school.teaAge)
    

	fmt.Printf("学校%s同学的年龄是%d\n",school.Student.stuName,school.stuAge)
	fmt.Printf("学校%s同学的性别是%s\n",school.Student.stuName,school.gender)

}

type School struct {
	name string
	address string
	Teacher
	Student
}

type Teacher struct {
	teaName string
	teaAge int
}

type Student struct {
	stuName string
	stuAge int
	gender string
}

学校张三老师的年龄是23
学校张三老师的年龄是23
学校小明同学的年龄是13
学校小明同学的性别是male

可见性

如果结构体名,字段名等是以小写字母开头,那么代表着private,其他包不可访问,如果大写,代表着public,其他包可访问。

二、方法

结构体就像是类的一种简化形式,那么类的方法在哪里呢?在Go语言中有一个概念,它和方法有着同样的名字,并且大体上意思相同:Go 方法是作用在接收器(receiver)上的一个函数,接收器是某种类型的变量。因此方法是一种特殊类型的函数。

方法需要有一个接收器,才可以被执行。

方法定义

方法的定义方式和函数基本相同,只是需要在前方加一个接收器

func (t  receiver_type) method_name() [return] {
	方法体...
}

以上面的Demo为例,增加一个方法,然后在主函数中执行它。

func main()  {
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	school.display()
}

//方法,接收器类型为School,无返回值
func (s School) display()  {
	fmt.Println("学校的信息:",s)
}


//学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}

我们可以看到Go语言的方法也是非侵入式的,没有显式的声明是哪个语言的方法,一切由接收器决定。

多态

我们再写一个func (t Teacher) display()方法,这是个同名方法,接收器是Teacher类型,同样可以执行。

func main()  {
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	school.display()
	t.display()
	
}

func (s School) display()  {
	fmt.Println("学校的信息:",s)
}
func (t Teacher) display()  {
	fmt.Println("学校的信息:",t)
}


//学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
//学校的信息: {张三 23}

我们把接收器为子类School的方法删除,只留下一个接收器为父类Teacher的方法,主函数不变。

此时的子类会先寻找接收器为本类型的方法(称为方法重写),如果没有,会寻找接收器为父类的方法,并执行。

由于接收器是父类,父类没有子类的部分字段,所以只会执行父类的内容。

这也是多态的一种体现,在其他语言中称为重载

func main()  {
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}
	
    //子类调用父类方法
	school.display()
	t.display()

}

func (t Teacher) display()  {
	fmt.Println("学校的信息:",t)
}

//学校的信息: {张三 23}
//学校的信息: {张三 23}

指针接收器与非指针接收器

接收器根据接收器的类型可以分为指针接收器、非指针接收器。两种接收器在使用时会产生不同的效果。根据效果的不同,两种接收器会被用于不同性能和功能要求的代码中。

如果写出以下两种方法的。接收器类型一个是指针,一个非指针。

func (s School) display()  {
	fmt.Println("学校的信息:",s)
}

func (s *School) display()  {
	fmt.Println("学校的信息:",s)
}

首先这样写是错的,这并不是多态性,因为这就是一个方法。

对于方法来说,接收器类型指针和非指针,都可以调用方法。两者可以互相转换。

非指针--指针

func main()  {
	
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	school.display()

}

func (s *School) display()  {
	fmt.Println("学校的信息:",s)
}

//学校的信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}

指针--指针

func main()  {
	school:=new(School)
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school=&School{"友谊小学","安徽省杭埠镇",t,s}

	school.display()

}

func (s *School) display()  {
	fmt.Println("学校的信息:",s)
}

////学校的信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}

指针--非指针

func main()  {
	school:=new(School)
	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school=&School{"友谊小学","安徽省杭埠镇",t,s}

	school.display()

}

func (s School) display()  {
	fmt.Println("学校的信息:",s)
}

//学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}

指针接收器

指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的 this 或者 self。

由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。

我们测试一下:增加一个变量a,对不同类型进行组合。

控制变量:以下每次只对接收器、接收器类型中的一个进行改变。

指针--非指针接收器

func main()  {

	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=&School{"友谊小学","安徽省杭埠镇",t,s}

	a:=school

	school.display()

	fmt.Println(school.name)
	fmt.Println(a.name)


	fmt.Println("学校信息:",a)

}

func (s School) display()  {
	s.name="爱心小学"
	fmt.Println("学校的信息:",s)
}

学校的信息: {爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
友谊小学
友谊小学
学校信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}


指针--指针接收器

func main()  {

	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=&School{"友谊小学","安徽省杭埠镇",t,s}

	a:=school

	school.display()

	fmt.Println(school.name)
	fmt.Println(a.name)


	fmt.Println("学校信息:",a)

}

func (s *School) display()  {
	s.name="爱心小学"
	fmt.Println("学校的信息:",s)
}


学校的信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
爱心小学
爱心小学
学校信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}



非指针--非指针接收器

func main()  {

	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	a:=school

	school.display()

	fmt.Println(school.name)
	fmt.Println(a.name)


	fmt.Println("学校信息:",a)

}

func (s *School) display()  {
	s.name="爱心小学"
	fmt.Println("学校的信息:",s)
}

学校的信息: {爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
友谊小学
友谊小学
学校信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}

非指针--指针接收器

func main()  {

	t:=Teacher{"张三",23}
	s:=Student{"小明",13,"male"}

	school:=School{"友谊小学","安徽省杭埠镇",t,s}

	a:=school

	school.display()

	fmt.Println(school.name)
	fmt.Println(a.name)


	fmt.Println("学校信息:",a)

}

func (s *School) display()  {
	s.name="爱心小学"
	fmt.Println("学校的信息:",s)
}


学校的信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
爱心小学
友谊小学
学校信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}


已经可以得出结论了,接收器类型指针和非指针之间在方法中可以互相转换。

如果方法的接收器类型为指针,那么方法中改变的值,方法结束后仍然有效。

如果方法的接收器类型为非指针,那么方法中改变的值,方法结束后无效。

方法的调用者类型不会被方法接收器类型修改。

posted @ 2020-11-25 11:10  cgl_dong  阅读(385)  评论(0编辑  收藏  举报