一尘不染

如何在Go界面中处理重复方法?

go

如何在Go界面中处理重复方法?

package main

import (
    "fmt"
)

type Person interface {
    Hello()
}

type Joker interface {
    Person
    Joke()
}

type Jumper interface {
    Person
    Jump()
}

type Entertainer interface {
    Joker
    Jumper
}

func main() {
    fmt.Println("hello, world")
}

如果我运行此代码,则会发生以下错误。

$ go run foo.go
# command-line-arguments
./foo.go:24: duplicate method Hello

如何处理这样的情况?在这种情况下如何避免重复方法?


阅读 292

收藏
2020-07-02

共1个答案

一尘不染

这样做的方法是显式提供所需的方法,而不是使用速记语法:

type Entertainer interface {
    Hello()
    Joke()
    Jump()
}

这看起来像是代码重复,但是请注意,重复代码在Go中并不是一件不典型的事情,尤其是当它导致更清晰的代码时。

另请注意:如果你想在其他语言中典型的继承权,它可能看起来像你做这个丢失一些信息,因为你没有记录的事实,Entertainer 继承
,比如说,从Person。但是Go接口纯粹是结构性的,没有继承。因为一个Entertainer具有Hello()方法,所以每个Entertainer自动为一个Person,无论您是否PersonEntertainer声明内部明确提及。

即使您不对任何接口使用简写语法,所有这些编译都不会出现问题(“声明的且未使用的”错误除外):

var e Entertainer
var ju Jumper
var jo Joker
var p Person

p = e    // every Entertainer is also a Person
p = ju   // every Jumper is also a Person
p = jo   // every Joker is also a Person

ju = e   // every Entertainer is also a Jumper

jo = e   // every Entertainer is also a Joker

这是一个完整的程序,可以编译并正常运行。鉴于以下声明:

package main

import (
    "fmt"
)

type Person interface {
    Hello()
}

type Joker interface {
    Hello()
    Joke()
}

type Jumper interface {
    Hello()
    Jump()
}

type Entertainer interface {
    Hello()
    Joke()
    Jump()
}

让我们创建一个Clown类型:

type Clown struct {}

func (c Clown) Hello() {
    fmt.Println("Hello everybody")
}

func (c Clown) Joke() {
    fmt.Println("I'm funny")
}

func (c Clown) Jump() {
    fmt.Println("And up I go")
}

A Clown可以打招呼,跳跃和开玩笑,因此它实现了我们所有的接口。鉴于以下四个功能:

func PersonSayHello(p Person) {
    p.Hello()
}

func JumperJump(j Jumper) {
    j.Jump()
}

func JokerJoke(j Joker) {
    j.Joke()
}

func EntertainerEntertain(e Entertainer) {
    e.Joke()
    e.Jump()
}

您可以将a传递Clown给其中任何一个:

func main() {
    c := Clown{}

    PersonSayHello(c)
    JokerJoke(c)
    JumperJump(c)
    EntertainerEntertain(c)
}

这是使用上面的代码转到Go Playground的链接

最后一件事–您可能会这样争论:“但是,如果我以后对进行更改Person,它将不会反映在其他界面中。” 的确,您必须手动进行这种调整,但是编译器会告知您。

如果具有此功能:

func JumperSayHello(j Jumper) {
    PersonSayHello(j)
}

您的代码将正常工作。但是,如果向中添加另一个方法Person,则依赖于a Jumper是a 的事实的代码Person将不再编译。用

type Person interface {
    Hello()
    Think()
}

你得到

。\ main.go:18:在PersonSayHello的参数中不能将j(跳线类型)用作Person类型:
        跳线未实现Person(缺少Think方法)

只要您在 任何地方 都有依赖于a Jumper始终为a
的事实的代码,就属于这种情况Person。而且,如果您不这样做,即使在测试中也没有,那么-也许,跳线不认为实际上并不重要?

但是,如果无论出于何种原因,无论您对这些接口进行了什么更改,您实际上都需要确保a Jumper始终为a
Person,但实际上并没有在任何地方使用此事实,则始终可以为此目的创建代码:

package main

type Person interface {
    Hello()
}

type Jumper interface {
    Hello()
    Jump()
}

// this function is never used, it just exists to ensure
// interface compatibility at compile time
func ensureJumperIsPerson(j Jumper) {
    var p Person = j
    _ = p
}

func main() {
}
2020-07-02