什么是接口

在Go语言中接口(interface)是一种类型,一种抽象的类型。

interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。

为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型。

type Cat struct{}

func (c Cat) Say() string { return "喵喵喵" }

type Dog struct{}

func (d Dog) Say() string { return "汪汪汪" }

func main() {
	c := Cat{}
	fmt.Println("猫:", c.Say())
	d := Dog{}
	fmt.Println("狗:", d.Say())
}

上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?

像类似的例子在我们编程过程中会经常遇到:

比如一个网上商城可能使用支付宝、微信、银联等方式去在线支付,我们能不能把它们当成“支付方式”来处理呢?

比如三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?

比如销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?

Go语言中为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。

实现

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表

// Sayer 接口
type Sayer interface {
	say()
}

type dog struct {}

type cat struct {}

// dog实现了Sayer接口
func (d dog) say() {
	fmt.Println("汪汪汪")
}

// cat实现了Sayer接口
func (c cat) say() {
	fmt.Println("喵喵喵")
}

接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

那实现了接口有什么用呢?

接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dogcat类型的变量。

func main() {
	var x Sayer // 声明一个Sayer类型的变量x
	a := cat{}  // 实例化一个cat
	b := dog{}  // 实例化一个dog
	x = a       // 可以把cat实例直接赋值给x
	x.say()     // 喵喵喵
	x = b       // 可以把dog实例直接赋值给x
	x.say()     // 汪汪汪
}

Error

除却空接口interface{}外,这应该是我们最常用到的接口了,众所周知Go没有抛出和处理异常的机制,所以我们经常就有看到Error的地方。

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}

从Go的源码我们可以看出,只要你实现了Error() string,你对应的类型就实现了error接口。

下面我们来正经的实验一下,首先建立一个结构体,然后实现Error()string方法。

type Human struct {
	Err error
}

func (h *Human)Error() string {
	return fmt.Sprintf("%v", h.Err)
}

然后我们给他实现一个方法。抛出Error,我们返回的类型是error,可以看到,我们如果实现了Error方法,那么该结构体也是属于此类型(PS: 接口是一种类型)。

func (h *Human)Say() error {
	for i:=0;i<5;i++ {
		if i == 3 {
			//return fmt.Errorf("Salmon Error")
			h.Err = fmt.Errorf("Salmon Error2")
			return h
		}
	}
	return nil
}

func main() {
	var h Human
	err := h.Say()
	if err != nil {
		fmt.Println(err)
	}else {
		fmt.Println("Not Error")
	}
}

看上去是不是很熟悉,这正是接口的实现的一些好处。