一尘不染

为什么在Golang中需要接口?

go

在Golang中,我们将结构体与接收器方法结合使用。到这里为止一切都很完美。
我不确定什么是接口。我们在结构中定义方法,如果要在结构上实现方法,则无论如何都要在另一个结构下再次编写该方法。
这意味着接口似乎只是方法定义,仅占用了页面上多余的空间。

有没有解释我为什么需要接口的示例?


阅读 688

收藏
2020-07-02

共1个答案

一尘不染

接口太大了,不能在这里给出全面的答案,但是有些事情需要弄清楚它们的用途。

接口是一种 工具
。是否使用它们取决于您自己,但是它们可以使代码更清晰,更短,更易读,并且它们可以在程序包,客户端(用户)和服务器(提供者)之间提供良好的API。

是的,您可以创建自己的struct类型,并且可以“附加”方法,例如:

type Cat struct{}

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

type Dog struct{}

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

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

我们已经在上面的代码中看到了一些重复:在使两者Cat同时Dog说话时。我们可以像 对待动物
一样对待两者吗?并不是的。当然,我们可以将两者都作为处理interface{},但是如果这样做,则不能调用它们的Say()方法,因为type的值interface{}未定义任何方法。

上面的两种类型都有一些 相似之处 :两者都具有Say()签名相同的方法(参数和结果类型)。我们可以通过一个接口 捕获 它:

type Sayer interface {
    Say() string
}

接口仅包含方法的 签名 ,而不包含方法的 实现

请注意,在Go语言中,如果类型的方法集是该接口的超集,则该类型 隐式
实现该接口。没有意图的声明。这是什么意思?我们以前CatDog类型已经实现了这个Sayer接口,即使在我们前面写了他们这个接口定义根本不存在,而我们没有接触他们,以纪念他们什么。他们只是做。

接口指定行为 。实现接口的类型意味着该类型具有接口“规定”的所有方法。

由于两者都实现了Sayer,我们可以将两者都当作的值来处理Sayer,因此它们具有相同的地方。看看我们如何统一处理这两个问题:

animals := []Sayer{c, d}
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

(这反映出部分只是为了获得类型名称,到目前为止还不多。)

最重要的部分是,我们可以同时处理CatDog视为同类(接口类型),并与他们合作/使用它们。如果您很快要使用Say()方法创建其他类型,则可以在Cat和旁边排队Dog

type Horse struct{}

func (h Horse) Say() string { return "neigh" }

animals = append(animals, Horse{})
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

假设您要编写其他适用于这些类型的代码。辅助功能:

func MakeCatTalk(c Cat) {
    fmt.Println("Cat says:", c.Say())
}

是的,上面的函数可以使用Cat,也可以不使用。如果您想要类似的东西,则必须为每种类型编写它。不用说这有多糟。

是的,您可以编写它来接受的参数interface{},并使用类型断言类型开关,这将减少辅助函数的数量,但看起来仍然很丑陋。

解决方案?是的,界面。只需声明该函数以采用接口类型的值即可,该接口类型定义了您要对其执行的操作,仅此而已:

func MakeTalk(s Sayer) {
    fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}

你可以调用这个函数的值CatDogHorse或任何其他类型的现在不知道,直到,有一个Say()方法。凉。

Go Playground上尝试这些示例。

2020-07-02