您现在的位置是:首页 >技术教程 >go破冰之旅·15·go语言interface、方法重点说明网站首页技术教程

go破冰之旅·15·go语言interface、方法重点说明

ProblemTerminator 2024-06-14 18:01:02
简介go破冰之旅·15·go语言interface、方法重点说明

一次5-10分钟即可搞定,实用+效率!

上文中,我们重点看了下数组与切片(三):go结构体一览

本文我们重点来看看go中的接口及方法等一系列玩法。

目录

快速弄通interface

方法、接收者及其变种


快速弄通interface

interface是go中一种数据类型,interface就是其关键字,可称之为“接口”。

接口用来定义一组与之需要匹配的共性行为,这些行为就是方法,任何其他某个类型只要实现了接口所定义的所有方法就相当于自动实现了这个接口。

我们以“动物及其行为”来模拟一下:


// 定义一个动物的基准接口,定义一个“吃”的行为
type IAnimals interface {
	Eat()
}

// 对于本案例来说要不要Animals 结构体都可
// 为了一起把“组合”模式结合进来,这样看起来更丰富
type Animals struct {
	Name string
}

// Cat将Animals组合了进来
type Cat struct {
	Animals
}

type Bird struct {
	Animals
}

我们让Cat和Bird都实现Eat方法:

func (a Cat) Eat() {
	fmt.Printf("I'm %s, eating a fish.
", a.Name)
}

func (a Bird) Eat() {
	fmt.Printf("I'm %s, eating a insect.
", a.Name)
}

怎么使用接口来完成方法的调用呢?分别以Cat和Bird说明:

	// 声明一个接口对象animal
	var animal IAnimals
	// 实例化为Cat
	animal = Cat{Animals{Name: "cat_john"}}
	// 调用基本行为eat进行“吃”,cat_john吃了一个鱼
	animal.Eat()

	// 可直接以具体类型实例化为Bird
	bird := new(Bird)
	// 鸟的名称
	bird.Name = "bird_frank"
	// bird_frank吃了一个虫子
	bird.Eat()

speed running:

	I'm cat_john, eating a fish.
	I'm bird_frank, eating a insect.

是不是很简单?

方法、接收者及其变种

在上述演示案例中要注意,Eat()不是普通的函数,而是方法,方法是具有“接收者”的函数,区别就在于函数名前面接收者的定义。

温馨提示:接口中方法的参数列表、返回值列表的定义方式和普通的函数一致。

那么方法的接收者只能是结构体吗?当然不是,你想怎么玩就怎么玩,以另外一种类型说明:

// 自定义的一个类型
type Status int

// Status类型的取值设定
const (
	Normal   Status = 0
	Abnormal Status = 1
	Unknown  Status = 2
)

// 对应的输出汉文的方法
type IStatusOut interface {
	String() string
}

我们让Status类型作为接收者,具体的方法如下:


func (s Status) String() (name string) {
	switch s {
	case Normal:
		name = "正常"
	case Abnormal:
		name = "异常"
	case Unknown:
		name = "检验中"
	default:
		name = "未知"
	}

	return name
}

使用:

	var x Status = Abnormal
	fmt.Printf("%d --> %s
", x, x.String())
	var y Status = Unknown
	fmt.Printf("%d --> %s
", y, y.String())

运行结果相比你已经猜到了:


1 --> 异常
2 --> 检验中

所以,自己想怎么玩就可以怎么玩,不用拘泥于其类型。

那么接收者能不能是指针类型呢?当然可以,而且和一般类型稍有区别:


type IUpdater interface {
	// 按自定义算法输入一个值,返回一个值
	Update()
	Get() int
}

type Updater struct {
	num int
}

func (u Updater) Update() {
	if u.num > 5 {
		u.num += 5
	} else if u.num < -5 {
		u.num = -5
	} else {
		u.num += 1
	}
}

func (u Updater) Get() int {
	return u.num
}

调用看结果:

	i1 := Updater{num: 1}
	i1.Update()                    // 修改num
	fmt.Printf("i1= %v
", i1.num) // i1= 1

	i2 := new(Updater) // i2是一个结构体指针,即指针类型
	i2.num = 1
	i2.Update()
	fmt.Printf("i2= %v
", i2.num) // i2= 1
	/*
	i1= 1
	i2= 1
	*/

可以看到两点:

1,Update()方法并没有修改成功num值;

2,虽然结构体实现了IUpdater接口,但当生成的对象是结构体指针时,也可以成功调用Update()方法,但因为接收者本身是结构体类型,因此并不能修改num。

接下来我们改一改接收者并进行调用:

func (u *Updater) Update() {
	if u.num > 5 {
		u.num += 5
	} else if u.num < -5 {
		u.num = -5
	} else {
		u.num += 1
	}
}


	// 接收者为指针类型,因此声明的i1需要实例化为结构体指针
	var i1 IUpdater
	i1 = &Updater{num: 1}            // 如果没有取址符,则编译不通过Cannot use 'Updater{num: 1}' (type Updater) as type IUpdater Type does not implement 'IUpdater' as 'Update' method has a pointer receiver
	i1.Update()                      // 修改num
	fmt.Printf("i1= %v
", i1.Get()) // i1= 2

	i2 := new(Updater) // i2就是一个结构体指针
	i2.num = 1
	i2.Update()
	fmt.Printf("i2= %v
", i2.num) // i2= 2

	// 不使用指针,直接实例化一个Updater对象
	i3 := Updater{num: 1}
	i3.Update()                      // 调用Update()
	fmt.Printf("i3= %v
", i3.Get()) // i3= 2
	/*
	i1= 2
	i2= 2
	i3= 2
	*/

可以看到两点:

1,Update()方法成功修改成功num值;

2,虽然结构体指针实现了IUpdater接口,但当生成的对象是结构体时,也可以成功调用Update()方法并完成修改。

下文,我们重点看接口嵌套、接口类型的综合应用,等你来哦!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。