接口的定义

Go 的接口是一种抽象类型,它定义了一组方法签名,用来表示一种行为或功能。 用type关键字定义的,后面跟着接口的名称和interface关键字。 然后在花括号中列出方法签名,方法签名包括方法名,参数列表和返回值列表。

// 定义一个动物的接口
type animal interface {
  // 动物可以叫
  speak() string;
  // 动物可以移动
  move() string;
}

接口中只有方法签名,没有方法体

接口的实现

在 Go 中,接口的实现是隐式的,只要一个类型拥有了接口所要求的所有方法,就可以认为它实现了该接口。 接口类型的变量可以存储任何实现了该接口的值。

// 定义一个狗的结构体
type dog struct {
  name string
}

// 为狗类型定义speak方法
func (d dog) speak() string {
  return "汪!"
}

// 为狗类型定义move方法
func (d dog) move() string {
  return "走"
}

// 定义一个猫的结构体
type cat struct {
  name string
}

// 为猫类型定义speak方法
func (c cat) speak() string {
  return "喵!"
}

// 为猫类型定义move方法
func (c cat) move() string {
  return "跳"
}

这两个类型分别定义了speakmove方法,使它们满足 animal 接口的要求。 所以,我们就可以说 dog 和 cat 都实现了 animal 接口。

接口的使用

我们可以使用接口类型作为函数的参数或返回值,来实现多态和代码复用。

// 定义一个函数,接受一个animal类型的参数,并打印它的speak和move结果
func describe(a animal) {
  fmt.Println(a.speak())
  fmt.Println(a.move())
}

func main() {
  d := dog{name: "旺财"}
  c := cat{name: "小花"}
}

// 调用describe函数,并传入dog对象
describe(d)
// 输出:
// 汪!
// 跳

// 调用describe函数,并传入cat对象
describe(c)
// 输出:
// 喵!
// 跳

由于 dog 和 cat 都实现了 animal 接口,所以它们都可以作为 describe 函数的参数。这样我们就实现了多态和代码复用,不需要针对每种动物都写一个 describe 函数。

空接口

空接口是一种特殊的接口类型,它没有定义任何方法,因此任何类型都可以实现它。 空接口可以用来表示任意类型的值。

var any interface{}

类型断言

类型断言是一种操作,用来检查一个接口类型的变量是否存储了一个特定类型的值,并且可以将该值提取出来。 类型断言的语法是:

x.(T)

其中 x 是一个接口类型的变量,T 是一个类型。

如果 x 存储了一个 T 类型的值,那么这个操作会返回该值和 true。 如果 x 没有存储一个 T 类型的值,那么这个操作会返回零值和 false。 如果不关心断言是否成功,可以只使用一个变量来接收返回值。 如果断言失败且没有使用第二个变量来接收返回值,那么程序会发生 panic。

类型切换

类型切换是一种语句,用来根据一个接口类型的变量的实际类型执行不同的分支。 类型切换的语法是:

switch x.(type) {
  case T1: ...
  case T2: ...
  default: ...
}

其中 x 是一个接口类型的变量,T1, T2 等是不同的类型。在每个分支中,x 会被转换为对应的类型,并且可以直接访问其方法或字段。如果没有匹配到任何分支,那么执行 default 分支。

接口嵌套

接口支持嵌套,可以将多个接口组合成一个新的接口。 接口嵌套的语法是:

type A interface {B; C; D}

其中 A, B, C, D 都是接口类型。这样定义后,A 就包含了 B, C, D 中定义的所有方法,并且任何实现了 A 的类型也必须实现 B, C, D 中定义的所有方法。