douyiqi9640 2014-01-16 15:35
浏览 322
已采纳

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

Why two named interfaces with same methods are treated as different ones - and how can you avoid that?

So let's say we have a guy who likes to eat products (Eater). He doesn't care what products he it, he only wants to be pointed out from where he can fetch new products. In other words he wants product service, but doesn't care what products product service will produce. In concrete implementation we will try to feed him with apples, so we will provide him with appleService.

Here is the result:

./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

Interfaces service.AppleI and eater.AppleI have same method Eat() and nothing else but golang considers them as different ones. Why and how to avoid that? According to duck typing this should work because what actually ProductServiceI requires is that provided struct has Eat() method - it shouldn't care about what name has interface (service.ProductI vs eater.ProductI).

Below is full code:

==> ./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()
}

==> ./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()
    }
}

==> ./apple/service/service.go <==

package service

import "./apple"

type ProductI interface {
    Eat()
}

type AppleService struct {
}

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

==> ./apple/service/apple/apple.go <==

package apple

import "fmt"

type Apple struct {
}

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

I thought it doesn't matter what name or what import path has interface as long as declaration is the same.

  • 写回答

3条回答 默认 最新

  • dongzhanlian6289 2014-01-16 22:13
    关注

    I will answer my own question

    Firstly I just found out answer about why those interfaces are not considered similar in go in their official faq: http://golang.org/doc/faq#t_and_equal_interface

    Secondly in my case I can use anonymous interface interface { Eat() } instead of declaring type ProductI interface { Eat() }

    But in case of examples from go faq that is not possible. Consider this:

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

    If you would use the same trick here you will get

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

    In other words you end up with recursion - ups

    Below is my updated code with this trick - it works - someone might find this trick useful.

    ==> ./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()
        }
    }
    

    ==> ./main.go <==

    package main
    
    import "./apple/service"
    import "./eater"
    
    func main() {
        appleService := &service.AppleService{}
        appleEater := eater.New(appleService)
        appleEater.EatUntilHappy()
    }
    

    ==> ./apple/service/service.go <==

    package service
    
    import "./apple"
    
    type AppleService struct {
    }
    
    func (a *AppleService) New() interface{ Eat() } {
        return &apple.Apple{}
    }
    

    ==> ./apple/service/apple/apple.go <==

    package apple
    
    import "fmt"
    
    type Apple struct {
    }
    
    func (a *Apple) Eat() {
        fmt.Println("mniam, mniam")
    }
    

    And the result :)

    # go run main.go
    mniam, mniam
    mniam, mniam
    mniam, mniam
    mniam, mniam
    mniam, mniam
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图