golang - 接口有相同的方法,但被认为是不同的

时间:2014-01-16 15:35:41

标签: go

为什么两个具有相同方法的命名接口被视为不同的接口 - 您如何避免这种情况?

因此,假设我们有一个喜欢吃产品的人(Eater)。他不关心他是什么产品,他只想指出他可以从哪里买到新产品。换句话说,他想要产品服务,但不关心产品服务会产生什么样的产品。在具体实现中,我们将尝试用苹果喂他,所以我们将为他提供appleService。

结果如下:

./main.go:9: cannot use appleService (type *service.AppleService) as type eater.ProductServiceI in function argument:
        *service.AppleService does not implement eater.ProductServiceI (wrong type for New method)
                have New() service.ProductI
                want New() eater.ProductI

接口service.AppleIeater.AppleI具有相同的方法Eat(),除了golang之外没有其他方法将它们视为不同的方法。为什么以及如何避免这种情况?根据duck typing这应该有效,因为实际上ProductServiceI需要的是提供的struct具有Eat()方法 - 它不应该关心具有接口的名称(service.ProductI vs eater.ProductI )。

以下是完整代码:

==&GT; ./main.go< ==

package main

import "./apple/service"
import "./eater"

func main() {
    appleService := &service.AppleService{}
    // func eater.New(productService ProductServiceI)
    appleEater := eater.New(appleService) 
    appleEater.EatUntilHappy()
}

==&GT; ./eater/eater.go< ==

package eater

type ProductServiceI interface {
    New() ProductI
}

type ProductI interface {
    Eat()
}

type Eater struct {
    productService ProductServiceI
}

func New(productService ProductServiceI) *Eater {
    return &Eater{
        productService: productService,
    }
}

func (a *Eater) EatUntilHappy() {
    for i:=0; i < 5; i++ {
        product := a.productService.New()
        product.Eat()
    }
}

==&GT; ./apple/service/service.go< ==

package service

import "./apple"

type ProductI interface {
    Eat()
}

type AppleService struct {
}

func (a *AppleService) New() ProductI {
    return &apple.Apple{}
}

==&GT; ./apple/service/apple/apple.go< ==

package apple

import "fmt"

type Apple struct {
}

func (a *Apple) Eat() {
    fmt.Println("mniam, mniam")
}

只要声明是相同的,我认为无论名称或导入路径具有什么接口都无关紧要。

3 个答案:

答案 0 :(得分:3)

我认为你在这里遇到了一些问题。例如,您已经定义了AppleI接口两次,一次在main中,一次在服务中。其次,您似乎正在尝试使用Python或Java(我猜测)样式包。值得一读packages

以下是编译和运行的代码版本。

==&GT;卡米尔/ main.go

package main

import (
    "kamil/service"
)

type Program struct {
    appleService service.AppleServiceI
}

func main() {
    program := &Program{
        appleService: &service.AppleService{},
    }

    apple := program.appleService.New()
    apple.Eat()
}

==&GT;卡米尔/服务/ service.go

package service

import (
    "kamil/service/apple"
)

type AppleServiceI interface {
    New() AppleI
}

type AppleI interface {
    Eat()
}

type AppleService struct {
}

func (a *AppleService) New() AppleI {
    return &apple.Apple{}
}

==&GT; kamil / service / apple / apple.go(未更改)

package apple

import "fmt"

type Apple struct {
}

func (a *Apple) Eat() {
    fmt.Println("mniam, mniam")
}

那就是说,我不认为你所采取的方法是惯用的GO,你可能会在某些时候最终碰到它:)

答案 1 :(得分:3)

我会回答我自己的问题

首先,我刚刚找到了关于为什么这些接口在其官方常见问题解答中被认为不相似的答案:http://golang.org/doc/faq#t_and_equal_interface

其次在我的情况下,我可以使用匿名界面interface { Eat() }而不是声明type ProductI interface { Eat() }

但是如果来自faq 的例子是不可能的。考虑一下:

type Equaler interface {
    Equal(Equaler) bool
}

type T int
func (t T) Equal(u Equaler) bool { return t == u.(T) }

如果你在这里使用相同的技巧,你将获得

type T int
func (t T) Equal(u interface{ Equal(interface{ Equal(...) bool }) bool }) bool { return t == u.(T) }

换句话说,你最终会得到递归 -

以下是我使用此技巧更新的代码 - 它有效 - 有人可能会觉得这个技巧很有用。

==&GT; ./eater/eater.go< ==

package eater

type ProductServiceI interface {
    New() interface { Eat() }
}

type Eater struct {
    productService ProductServiceI
}

func New(productService ProductServiceI) *Eater {
    return &Eater{
        productService: productService,
    }
}

func (a *Eater) EatUntilHappy() {
    for i:=0; i < 5; i++ {
        product := a.productService.New()
        product.Eat()
    }
}

==&GT; ./main.go< ==

package main

import "./apple/service"
import "./eater"

func main() {
    appleService := &service.AppleService{}
    appleEater := eater.New(appleService)
    appleEater.EatUntilHappy()
}

==&GT; ./apple/service/service.go< ==

package service

import "./apple"

type AppleService struct {
}

func (a *AppleService) New() interface{ Eat() } {
    return &apple.Apple{}
}

==&GT; ./apple/service/apple/apple.go< ==

package apple

import "fmt"

type Apple struct {
}

func (a *Apple) Eat() {
    fmt.Println("mniam, mniam")
}

结果:)

# go run main.go
mniam, mniam
mniam, mniam
mniam, mniam
mniam, mniam
mniam, mniam

答案 2 :(得分:1)

只是为了完成:如果在接口中使用私有方法(小写方法名称),则会出现非常相似的情况(具有相同签名的方法,但Go找不到它们)。

这样Go将对方法的搜索限制为定义接口的包,因此在实现该方法的包中找不到该方法。

实际上,因为我无法想到在公共接口中使用私有方法的任何合理理由:对接口方法使用与接口本身相同的可见性。