Go 使用更广泛的方法签名实现接口

Go 使用更广泛的方法签名实现接口,go,Go,在Go中,是否有一种方法可以使用方法实现接口,其中实现中相应方法的返回类型比预期的返回类型“宽” 这很难解释,所以这里有一个例子。我在Go Playerly中运行以下示例代码时遇到此错误: ./prog.go:36:14: cannot use BirdImpl{} (type BirdImpl) as type Animal in argument to foo: BirdImpl does not implement Animal (wrong type for Move metho

在Go中,是否有一种方法可以使用方法实现接口,其中实现中相应方法的返回类型比预期的返回类型“宽”

这很难解释,所以这里有一个例子。我在Go Playerly中运行以下示例代码时遇到此错误:

./prog.go:36:14: cannot use BirdImpl{} (type BirdImpl) as type Animal in argument to foo:
    BirdImpl does not implement Animal (wrong type for Move method)
        have Move() BirdMoveResult
        want Move() AnimalMoveResult
(其中,
BirdMoveResult
比“
AnimalMoveResult
更宽,因为
BirdMoveResult
的任何实现也是
AnimalMoveResult
的实现)

<>我知道,由于不同的返回类型,“代码> MOVER())/代码>方法签名完全不匹配,因此GO不考虑<代码> ByDimpLP/COD>作为<代码>动物< /代码>的实现。但是,如果Go比较返回类型,则任何
BirdMoveResult
的实现也将实现
AnimalMoveResult
。那么,
Move()BirdMoveResult
不应该是
Move()AnimalMoveResult
的可接受实现吗(如果不是,为什么不是)


编辑:在实际场景中,
Animal
animaloveresult
foo
是外部包的一部分。在我自己的代码中,我希望能够使用自己的接口方法扩展
AnimalMoveResult
(正如在
BirdMoveResult
示例中所做的),同时仍然能够使用扩展接口使用
foo

但是,您可以声明该方法以满足接口的要求,但可以返回一个更广泛的实现,该实现可以被类型断言

在Go中,是否有一种方法可以使用方法实现接口,其中实现中相应方法的返回类型比预期的返回类型“宽”


不可以。方法签名必须完全匹配。Go没有协变或逆变的概念。

这些问题通常是由以下原因造成的:

  • 过早接口
  • 假接口,或“气泡包装”(见我的咆哮)
  • 并行接口
您的错误与第三个问题最为相关,但每个错误都出现在您的示例设计中

好的接口应该传达(以其名称和方法签名)一组行为。接口的名称应该由它所包含的方法暗示,因为该名称只是用来捕获该行为的本质

为此目的似乎需要的唯一接口是:

类型移动器接口{
Move()MoveResult
}
对于MoveResult,您可以这样做(如果愿意,可以用int交换float):

类型MoveResult结构{
距离,高度
}
考虑到以下假设,此类型可能作为结构正常工作:

  • 移动结果只是数据点(它们不需要是动态的;您可以设置值并保留它们)
  • “额外”数据(如高度)在未指定时正确使用零值
现在,实现bird类型:

类型Bird结构{
//一些内部数据
}
func(b*Bird)Move()MoveResult{
//一些运动计算
返回MoveResult{距离:多远,高度:多高}
}
现在实现foo:

func foo(移动器)浮动64{
返回m.Move()距离
}
现在主要使用它:

func main(){
foo(&Bird{})
}
现在我们可以在程序中添加其他类型的
Mover
s,并将它们与
foo
一起使用,而不会出现问题


处理您的评论,其中
Animal
AnimalMoveResult
foo
都是外部的,您不能修改它们:

我的理解是如何提出问题的:有一个函数
foo
,设计用于接受一个名为
Animal
的接口,其特点是它如何返回专有类型。您希望将您的类型
Bird
用作
动物
,但您无法将您想要的所有行为都纳入该专有返回类型

那么如何解决这个问题呢

实现您自己的、更广泛的返回类型

类型birdOversult结构{
//一些内部数据
}
func(r birdMoveResult)GetDistance()int{
//保持距离
}
func(r birdMoveResult)GetHeight()int{
//获得高度
}
现在,实现Bird类型

类型Bird结构{
//一些内部数据
}
func(b*Bird)Move()动物结果{
var结果BIRDMOVERSULT
//计算移动结果
回鸟结果
}
请注意,
Move
返回
AnimalMoveResult
类型,因此现在
Bird
将满足
Animal
接口。我们之所以能够这样做,是因为
birdMoveResult
满足
AnimalMoveresult
类型,所以在这里返回它是合法的

剩下的问题是
Bird
Move
方法缩小了广泛的界面,实际上我们已经失去了您添加的新功能。为了在仍然满足
Animal
界面的情况下将其取回,我们根本不能更改
Move
方法签名。这里有两种可能的解决方法

  • 当使用bird类型时,您可以修改结果以访问额外的方法
  • var b Bird
    // ...
    高度:=b.Move()(birdMoveResult).GetHeight()
    
  • Bird
    添加一个新方法,该方法返回更宽的类型,使现有的
    Move
    具有相同的签名
  • func(b*Bird)MoveFull()birdMoveResult{
    var结果BIRDMOVERSULT
    //计算移动结果
    回鸟结果
    }
    func(b*Bird)Move()动物结果{
    返回b.MoveFull()
    }
    
    height:=b.MoveFull().GetHeight()
    
    这两种方法中的任何一种都会起作用
    package main
    
    type (
        Animal interface {
            Move() AnimalMoveResult
        }
        Bird interface {
            Move() BirdMoveResult
        }
        AnimalMoveResult interface {
            GetDistance() int
        }
        BirdMoveResult interface {
            GetDistance() int
            GetHeight() int
        }
        BirdImpl struct{}
        BirdMoveResultImpl struct{}
    )
    
    // Some implementation of BirdImpl.Move
    // Some implementation of BirdMoveResultImpl.GetDistance
    // Some implementation of BirdMoveResultImpl.GetHeight
    
    func foo(animal Animal) int {
        return animal.Move().GetDistance()
    }
    
    func main() {
        foo(BirdImpl{})  // This fails because BirdImpl doesn't implement Animal. My question is why not?
    }