go - Go接口(interface)中如何处理重复的方法?

标签 go interface

Go接口(interface)中如何处理重复的方法?

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

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

最佳答案

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

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

这看起来像是代码重复,但请注意,重复代码在 Go 中并非不常见,尤其是当它导致更清晰的代码时。

另请注意:如果您从其他语言的典型继承的角度考虑,这样做似乎会丢失一些信息,因为您没有记录 Entertainer 的事实继承,比如说,。但是 Go 接口(interface)是纯结构的,没有继承。因为 Entertainer 有一个 Hello() 方法,所以每个 Entertainer 都会自动成为一个 Person,无论您是否显式在 Entertainer 声明中提及 Person

即使您不对任何接口(interface)使用速记语法,所有这些都可以毫无问题地编译(除了“已声明且未使用”错误):

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")
}

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

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()
}

你可以传递一个 Clown 给他们中的任何一个:

func main() {
    c := Clown{}

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

Here's a link to a Go Playground with the above code .

最后一件事 – 你可以这样争论:“但如果我稍后对 Person 进行更改,它不会反射(reflect)在其他界面中。”确实,您必须手动进行这样的调整,但编译器会让您知道。

如果你有这个功能:

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

您的代码可以正常运行。但是,如果您向 Person 添加另一个方法,则依赖于 JumperPerson 这一事实的代码将不再编译。与

type Person interface {
    Hello()
    Think()
}

你得到

.\main.go:18: cannot use j (type Jumper) as type Person in argument to PersonSayHello:
        Jumper does not implement Person (missing Think method)

This will be the case as long as you have code anywhere that relies on the fact that a Jumper is always a Person. And if you don't, not even in your tests, then – well, maybe it doesn't actually matter that the jumper doesn't think?

But if for whatever reason you actually need to ensure that a Jumper is always a Person, no matter what changes you make to these interfaces, but this fact isn't actually used anywhere, you can always create code just for this purpose:

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() {
}

关于go - Go接口(interface)中如何处理重复的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43730255/

相关文章:

go - 如何转换具有相同签名的函数类型?

json - Go JSON解析错误

java - 为什么Java中没有多重继承,却允许实现多个接口(interface)?

interface - 使用协议(protocol) 'static' 在 Clojure 中描述调度是否准确?

bash - 如何在Ubuntu上正确安装protoc-gen-go?

dictionary - 构造深层结构不起作用

pointers - 如何检测接口(interface){}是否为指针?

java - DAO 接口(interface)

java - 从 ExpandableListView 调用的 Fragment Communicator 接口(interface)不起作用

java - 'instanceof' 运算符对接口(interface)和类的行为不同