七、面向对象

  • 严格意义上说,GO语言中没有类(class)的概念,但是我们可以将结构体比作为类,因为在结构体中可以添加属性(成员),方法(函数)。
  • 当然严格意义上,GO语言中是没有继承的,但是我们可以通过”匿名组合”来实现继承的效果。

1.继承

//继承
type Person struct {
	id   int
	name string
	age  int
}

/**
	通过匿名字段实现了继承,将公共的属性封装在Person中,在Student中直接包含Person,那么Student中就有了Person中所有的成员,Person就是匿名字段。
 */
type Student struct {
	Person
	score float32
}

func main() {
	//顺序初始化
	s1 := Student{Person{1, "张三", 28}, 97}
	fmt.Println(s1) //{{1 张三 28} 97}
	
	//s1.Person.name 和 s1.name是一样的效果。如果在Student中也加入了一个成员name,这样与Person中的name重名了。
	//基本的原则:如果能够在自己对象所属的类(结构体)中找到对应的成员,那么直接进行操作,如果找不到就去对应的父类(结构体)中查找。这就是所谓的就近原则。
	fmt.Println(s1.id)  //1
	fmt.Println(s1.name)  //张三
	fmt.Println(s1.age)  //28
	fmt.Println(s1.score) //97	

	//指定初始化成员
	s2 := Student{Person: Person{name: "李四"}, score: 88}
	fmt.Println(s2) //{{0 李四 0} 88}

	//成员操作
	s2.Person.id = 2
	s2.Person.age = 22
	fmt.Println(s2) //{{2 李四 22} 88}
	s2.Person = Person{3, "王五", 26}
	fmt.Println(s2) //{{3 王五 26} 88}
}
1.1 指针类型匿名字段
type Person struct {
	id   int
	name string
	age  int
}

type Student struct {
	*Person
	score float32
}

func main() {
	s1 := Student{&Person{1, "张三", 28}, 97}
	fmt.Println(s1)                                                    //{0xc04204a3a0 97}
	fmt.Println(s1.id, s1.name, s1.age, s1.score)                      //1 张三 28 97
	fmt.Println(s1.Person.id, s1.Person.name, s1.Person.age, s1.score) //1 张三 28 97

	var s2 Student
	s2.Person = new(Person)
	s2.id = 2
	s2.name = "李四"
	s2.age = 22
	s2.score = 88
	fmt.Println(s2.id, s2.name, s2.age, s2.score) //2 李四 22 88
}

2.方法

  • 方法,大家可以理解成就是函数,但是在定义使用方面与前面讲解的函数还是有区别的。
  • GO中没有方法重载(所谓重载,指的是方法名称一致,参数类型,个数不一致)。
type Integer int //给int类型指定了一个别名叫Integer,别名可以随便起,只要符合GO语言的命名规则就可以。

//定义了一个方法,方法的定义与函数的区别
func (a Integer) Sum(b Integer) Integer {
	return a + b
}

func main() {
	var num Integer = 2
	result := num.Sum(3)
	fmt.Println(result) //5
}
2.1 给结构体添加方法
  • 接收者为普通变量,非指针,是值传递
  • 接收者为指针变量,引用传递
type Person struct {
	id   int
	name string
}

type Student struct {
	Person
	score float32
}

/*
	如果给结构体(类)加上了方法,那么根据结构体(类)创建完成对象后,是不是就可以通过对象加上“点”,就可以完成方法的调用。
	这与调用类中定义的属性的方式是完全一样的。这样就完成了通过方法与属性来描述一个对象的操作。
*/
func (s Student) ShowInfo() {
	fmt.Println(s.id, s.name, s.score)
}

//要修改其对应的值,将方法的接收者,修改成对应的指针类型。
func (s *Student) ModifyInfo(id int, name string, score float32) {
	s.id = id
	s.name = name
	s.score = score
}

func main() {
	s1 := Student{Person{1, "张三"}, 97}
	s1.ShowInfo() //1 张三 97

	(&s1).ModifyInfo(2, "李四", 88)
	fmt.Println(s1.id, s1.name, s1.score) //2 李四 88
}
2.2 指针变量的方法值
  • 在上面的案例中,我们定义了两个方法,一个是ShowInfo( ), 该方法的接收者为普通方法,一个ModifyInfo( )方法,该方法的接收者为指针变量
  • 通过测试,发现结构体指针变量,可以调用PrintShow( )方法呢?原因是:先将指针s1, 转换成*s1在调用。等价于:(*(&s1)).ShowInfo()
  • 如果是普通的结构体变量也能调用ModifyInfo( )方法。原因是:将普通的结构体类型的变量转换成(&s1)在调用ModifyInfo( )方法。等价于:(&s1).ModifyInfo(1, “zhagnsan”, 99)
  • 这样的好处是非常灵活,创建完对应的对象后,可以随意调用方法,不需要考虑太多指针的问题。
func main() {
	s1 := Student{Person{1, "张三"}, 97}
	s1.ModifyInfo(1, "zhagnsan", 99)
	s1.ShowInfo() //1 zhagnsan 99

	(&s1).ModifyInfo(2, "李四", 88)
	(&s1).ShowInfo() //2 李四 88
}

3.方法继承

type Person struct {
	id   int
	name string
}

func (p *Person) ModifyInfo(id int, name string) {
	p.id = id
	p.name = name
}

type Student struct {
	Person
	score float32
}

func main() {
	s := Student{Person{1, "张三"}, 97}
	s.ModifyInfo(1, "李四")
	fmt.Println(s.id, s.name, s.score)  //1 李四 97
}

4.方法重写

  • GO中没有方法重载(所谓重载,指的是方法名称一致,参数类型,个数不一致)。
type Person struct {
	id   int
	name string
}

func (p *Person) ModifyInfo(id int, name string) {
	p.id = id
	p.name = name
}

type Student struct {
	Person
	score float32
}

func (s *Student) ModifyInfo(id int, name string) {
	s.id = id
	s.name = "学生:"+name
}

func main() {
	s := Student{Person{1, "张三"}, 97}
	s.ModifyInfo(1, "李四")
	fmt.Println(s.id, s.name, s.score)  //1 学生:李四 97
}

5.接口

type Humaner interface {
	SayHi()
}

type Student struct {
	id   int
	name string
}

func (s *Student) SayHi() {
	fmt.Println(s.id, s.name)
}

type Teacher struct {
	name string
	sex  string
}

func (t *Teacher) SayHi() {
	fmt.Println(t.name, t.sex)
}

func main() {
	var i Humaner
	s := &Student{1, "张三"}
	t := &Teacher{"张老师", "男"}

	//只要类(结构体)实现对应的接口,那么根据该类创建的对象,可以赋值给对应的接口类型。
	i = s
	i.SayHi() //1 张三
	i = t
	i.SayHi() //张老师 男
}

6.多态

type Humaner interface {
	SayHi()
}

type Student struct {
	id   int
	name string
}

func (s *Student) SayHi() {
	fmt.Println(s.id, s.name)
}

type Teacher struct {
	name string
	sex  string
}

func (t *Teacher) SayHi() {
	fmt.Println(t.name, t.sex)
}

func WhoSayHi(i Humaner) {
	i.SayHi()
}

func main() {
	s := &Student{1, "张三"}
	t := &Teacher{"张老师", "男"}

	WhoSayHi(s) //1 张三
	WhoSayHi(t) //张老师 男
}
6.1多态案例
  • 实现一个计算器功能案例
package main

import "fmt"

//定义操作父类
type Operation struct {
	numA float32
	numB float32
}

//定义接口
type Getresulter interface {
	GetResult() float32 //计算器:方法有返回值
}

//继承:加法
type OperationAdd struct {
	Operation
}

//计算器:加法实现接口
func (a *OperationAdd) GetResult() float32 {
	return a.numA + a.numB
}

//继承:减法
type OperationSub struct {
	Operation
}

//计算器:减法实现接口
func (a *OperationSub) GetResult() float32 {
	return a.numA - a.numB
}

//创建一个工厂类
type OperationFactory struct {
}

//工厂类添加方法
func (f *OperationFactory) CreateOption(option string, numA float32, numB float32) float32 {
	var result float32
	switch option {
	case "+":
		add := &OperationAdd{Operation{numA, numB}}
		result = OperationWho(add)
	case "-":
		sub := &OperationSub{Operation{numA, numB}}
		result = OperationWho(sub)
	}
	return result
}

//多态调用计算器方法
func OperationWho(i Getresulter) float32 {
	return i.GetResult()
}

func main() {
	var opfactory OperationFactory
	add := opfactory.CreateOption("+", 10, 20) //加法
	fmt.Println(add)                           //30

	sub := opfactory.CreateOption("-", 10, 20) //减法
	fmt.Println(sub)                           //-10
}

7.空接口

  • 空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。
  • 当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库fmt中PrintXXX系列的函数,例如:
  • func Printf(fmt string, args …interface{})
  • func Println(args …interface{})
  • 如果自己定义函数,可以如下:
func Test(arg ...interface{}) {

}
  • Test( )函数可以接收任意个数,任意类型的参数。
func main() {
	//空接口万能类型,保存任意类型的值
	var i interface{} = 1
	fmt.Println(i) //1

	i = "abc"
	fmt.Println(i) //abc
}
7.1类型断言
func main() {
	arrs := make([]interface{}, 3)
	arrs[0] = 123
	arrs[1] = 3.14
	arrs[2] = "hello"

	for _, v := range arrs {
		//对数据v进行类型断言
		if data, ok := v.(int); ok {
			fmt.Println(data)
		} else if data, ok := v.(float64); ok {
			fmt.Println(data)
		} else if data, ok := v.(string); ok {
			fmt.Println(data)
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值