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

时间:2017-05-02 04:46:41

标签: go interface

如何处理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

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

3 个答案:

答案 0 :(得分:8)

执行此操作的方法是显式提供所需的方法,而不是使用简写语法:

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

这可能看起来像代码重复,但请注意重复的代码在Go中并不是非典型的东西,特别是当它导致更清晰的代码时。

另请注意:如果您考虑使用其他语言的典型继承,看起来您可能会丢失一些信息,因为您没有记录Entertainer 的事实从Person继承。但Go接口纯粹是结构化的,没有继承。由于Entertainer采用Hello()方法,因此无论您是否明确提及Entertainer Person声明Person,每个Entertainer都自动为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

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) } 传递给其中任何一个:

Person

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

最后一件事 - 你可以争论这样的事情:“但是如果我后来对func JumperSayHello(j Jumper) { PersonSayHello(j) } 进行了更改,它将不会反映在其他界面中。”确实如此,你必须手动进行这样的调整,但编译器会让你知道它。

如果你有这个功能:

Person

您的代码可以正常运行。但是,如果向Jumper添加另一个方法,则依赖于Persontype Person interface { Hello() Think() } 的事实的代码将不再编译。与

Jumper

你得到了

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

只要你有任何地方的代码,依赖于Person始终是Jumper的事实,就会出现这种情况。如果你不这样做,甚至在你的测试中,那么 - 好吧,也许跳线没有想到并不重要?

但是,无论出于何种原因,你确实需要确保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() { } ,无论你对这些接口做了什么改变,但这个事实实际上并没有在任何地方使用,你总是可以为此目的创建代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
</head>
 <body ng-app="myApp">

   <my-custom-element ng-click="runAlert()"></my-custom-element>


  </body>
</html>

答案 1 :(得分:2)

我认为不可能这样做。 IMO,界面嵌入只是直接在那里使用这些功能的简写。所以它等同于具有两个Hello()函数。因此来自编译器的错误。

答案 2 :(得分:0)

后跟“ issue 6997, proposal: spec: allow embedding overlapping interfaces”。

  

如果您将接口视为对实现类型的一组约束,则   组合两个互不兼容的接口,例如:

type I interface { f(); String() string }
type J interface { g(); String() string } 
     

具有自然解释,等效于包含以下内容的并集的接口   这样的约束。例如这些应该等效:

type IJ interface { I; J }
type IJ interface { f(); g(); String() string }
     

但实际上第一个是错误:“重复方法:字符串”。

Go 1.14(五年后的2020年第一季度)可能包括一些改进:

CL 190258: allow embedding overlapping interfaces,替换为CL 191257

  

如果嵌入式方法具有与现有方法相同的签名,请从嵌入式接口中静默删除它们。

     

此调整并没有完全调整以前的仅基于语法的方法集计算,在此方法中方法没有签名信息(因此,根据新规则进行重复数据删除会有些棘手,无法正确执行),此更改完全重写了接口方法集计算,从cmd/compiler的实现中获取一页。

     

在第一遍中,当收集类型检查接口,显式方法和嵌入式接口,但未“扩展”接口时,也就是说,最终的方法集计算是延迟进行的,无论是在方法查找时还是在需要时,   在类型检查结束时。

规范已更新(CL 190378

看到一些examples here