简单的界面

在 Go 中,接口只是一组方法。我们使用接口来指定给定对象的行为。

type Painter interface {
    Paint()
}

实现类型不需要声明它正在实现接口。定义相同签名的方法就足够了。

type Rembrandt struct{}

func (r Rembrandt) Paint() {
    // use a lot of canvas here 
}

现在我们可以将结构用作接口。

var p Painter
p = Rembrandt{}

可以通过任意数量的类型来满足(或实现)接口。类型也可以实现任意数量的接口。

type Singer interface {
     Sing()
}

type Writer interface {
     Write()
}

type Human struct{}

func (h *Human) Sing() {
    fmt.Println("singing")
}

func (h *Human) Write() {
    fmt.Println("writing")
}

type OnlySinger struct{}
func (o *OnlySinger) Sing() {
    fmt.Println("singing")
}

这里,Human 结构同时满足 SingerWriter 接口,但 OnlySinger 结构只满足 Singer 接口。

空接口

有一个空的接口类型,它不包含任何方法。我们声明它为 interface{}。这不包含任何方法,因此每个 type 都满足它。因此,空接口可以包含任何类型值。

var a interface{}
var i int = 5
s := "Hello world"

type StructType struct {
    i, j int
    k string
}

// all are valid statements
a = i
a = s
a = &StructType{1, 2, "hello"}

接口最常见的用例是确保变量支持一个或多个行为。相比之下,空接口的主要用例是定义一个可以保存任何值的变量,而不管其具体类型如何。

要将这些值恢复为原始类型,我们只需要这样做

i = a.(int)
s = a.(string)
m := a.(*StructType)

要么

i, ok := a.(int)
s, ok := a.(string)
m, ok := a.(*StructType)

ok 表示 interface a 是否可转换为给定类型。如果不可能施展 ok 将是 false

接口值

如果声明接口的变量,它可以存储实现接口声明的方法的任何值类型!

如果我们声明 interface Singerh,它可以存储 HumanOnlySinger. 类型的值。这是因为它们都实现了 Singer 接口指定的方法。

var h Singer
h = &human{}

h.Sing()