接口类型是对其他类型“行为”的抽象和概括。go语言中的接口interface不仅是一组方法,还是一种内置类型,可以出现在变量定义、函数入参和返回值中。go语言中的接口通过一组方法(可以不包含任何方法,即interface{})指定一个对象的行为,接口的引入可以使得我们可以更好地组织编写出易于测试的代码。

1、接口的定义:

在go中定义接口,使用interface关键字,在接口中只能定义实现的方法,而不能包含任何变量或字段,示例如下:

type Bird interface {
    Singing()
}

接口也可以是不带任何方法的空接口interface{}类型。但是不能对空接口持有的值做任何操作,因为空接口没有任何方法,但可以通过类型断言将接口断言为某一具体类型以获取方法。

2、接口的实现:

在go语言中所有接口的实现都是隐式的,任何类型只要实现了接口的所有方法,那么就实现了这个接口。一个类型可以实现多个接口,且一个接口的方法是实现这个接口的类型的方法的子集。示例如下:

type Students struct {
    Name string
}

func (student *Students) Singing() {
  fmt.Printf("%s can sing!\n", student.Name)
}

func (student *Students) Studying() {
  fmt.Printf("%s can study very well!\n", student.Name)
}

Students类型实现了Singing()方法,因此实现了Bird接口;同时Students还有Studying()方法。另外,实现接口时尽量使用结构体指针,以提高效率,节省资源(因为go语言的函数调用是传值的,是一个copy)。

3、非侵入式接口:

与其他语言不同的侵入式接口(实现接口时必须明确声明实现了该接口,如Java的implements~,即使有两个接口(A、B)完全一致只是不在一个命名空间,那么如果只声明类型C实现了A,则C并未实现B)不同,go语言的接口是非侵入式接口(只要实现了一个接口的所有方法,则实现了该接口,不必显式声明实现了某个接口)。

4、接口赋值:

接口赋值,可以将对象实例赋给接口,也可将一个接口赋值给另一个接口(在go中,只要两个接口有相同的方法列表,则它们是相同的,可以相互赋值。另外,接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么B可以赋值给A)。

5、接口组合:

go语言支持接口组合。例如go语言中有io.Reader和io.Writer以及io.ReadWriter。其中io.ReadWriter接口的定义如下:

type ReadWriter interface {
    Reader
    Writer
}

6、接口值及接口值的可较性:

接口值是指一个具体的类型及该类型的值,分别被称为接口的动态类型和动态值。一个接口的零值是其类型和值的部分都是nil,调用一个空接口的任意方法都将panic。

接口值可以用==和!=进行比较,两个接口值相等当且仅当它们都是nil或其动态类型相同且动态值==操作相等。但是,如果两个接口值得动态类型相同,但动态类型不可比较,则比较接口会产生panic。

一个不包含任何值得nil接口与一个刚好包含nil指针的接口值不同。

7、类型断言:

类似x.(T)的语句被称为类型断言,x表示一个接口类型,T表示一个具体类型。类型断言操作将检查该接口的动态类型是否和断言的类型匹配,如果匹配则断言成功。类型断言一般配合ok使用,即先判断类型断言是否成功,如果成功才可继续操作,否则易引起panic。