实现具有更广泛方法签名的接口

在Go中,有没有办法使用方法实现接口,其中实现中相应方法的返回类型是“比”预期的返回类型“宽”的?


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


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

(其中 是 “wide than” 因为 任何 实现也是BirdMoveResultAnimalMoveResultBirdMoveResultAnimalMoveResult)


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?

}


我知道由于返回类型不同,方法签名不完全匹配,因此Go不考虑作为的实现。但是,如果 Go 比较返回类型,则 的任何实现也会实现 。那么,不应该是一个可接受的实现(如果不是,为什么不呢)?Move()BirdImplAnimalBirdMoveResultAnimalMoveResultMove() BirdMoveResultMove() AnimalMoveResult


编辑:在实际方案中,、 和 是外部包的一部分。在我自己的代码中,我希望能够使用我自己的接口方法进行扩展(如示例中所做的那样),同时仍然能够使用扩展接口来使用。AnimalAnimalMoveResultfooAnimalMoveResultBirdMoveResultfoo


桃花长相依
浏览 76回答 3
3回答

蝴蝶不菲

这些类型的问题通常是以下结果:过早的接口假界面,或“气泡包装”(请参阅我的咆哮 这里)并行接口您遇到的错误与第 3 个问题最具体相关,但每个错误都存在于此处的示例设计中。好的接口应该传达(以它们的名称和方法签名)一组行为。接口的名称应由它包含的方法暗示,因为该名称只是应该捕获该行为的本质。为此目的似乎唯一需要的接口是:type Mover interface {    Move() MoveResult}对于 MoveResult,您可以执行此操作(如果您愿意,可以使用 int 交换 float):type MoveResult struct {    Distance, Height float64}给定以下假设,此类型可以作为结构很好地发挥作用:移动结果只是数据点(它们不需要是动态的;您可以设置值并保留它们)“额外”数据(如高度)在未指定时以零值正确运行。现在,实现 bird 类型:type Bird struct {    // some internal data}func (b *Bird) Move() MoveResult {    // some movement calculations    return MoveResult{Distance: howFar, Height: howHigh}}现在实现 foo:func foo(m Mover) float64 {    return m.Move().Distance}现在主要使用它:func main() {    foo(&Bird{})}现在我们可以将其他类型的s添加到程序中,并毫无问题地使用它们。Moverfoo解决您的注释,其中 和 都是外部的,您无法修改它们:AnimalAnimalMoveResultfoo我如何用我的理解提出问题:有这个函数,旨在接受一个名为的接口,其特征是它如何返回专有类型。您希望将类型用作 ,但无法将所需的所有行为都适合该专有返回类型。fooAnimalBirdAnimal那么如何解决这个问题呢?实现您自己的、更广泛的返回类型type birdMoveResult struct {    // some internal data}func (r birdMoveResult) GetDistance() int {    // get the distance}func (r birdMoveResult) GetHeight() int {    // get the height}现在,实现 Bird 类型type Bird struct {    // some internal data}func (b *Bird) Move() AnimalMoveResult {    var result birdMoveResult    // calculate the move result    return birdMoveResult}请注意,返回类型,因此现在将满足接口。我们能够做到这一点,因为满足类型,所以在这里返回它是合法的。MoveAnimalMoveResultBirdAnimalbirdMoveResultAnimalMoveresult剩下的问题是 s 方法缩小了宽界面的范围,我们实际上已经丢失了您添加的新功能。为了在仍然满足接口的同时将其取回,我们根本无法更改方法签名。以下是 2 种可能的解决方法。BirdMoveAnimalMove使用 bird 类型时,您可以对结果进行类型断言,以访问额外的方法var b Bird// ...height := b.Move().(birdMoveResult).GetHeight()向中添加一个新方法,该方法返回更广泛的类型,使现有方法具有相同的签名BirdMovefunc (b *Bird) MoveFull() birdMoveResult {    var result birdMoveResult    // calculate the move result    return birdMoveResult}func (b *Bird) Move() AnimalMoveResult {    return b.MoveFull()}height := b.MoveFull().GetHeight()这些都可以工作,这主要是偏好问题。

大话西游666

在Go中,有没有办法使用方法实现接口,其中实现中相应方法的返回类型是“比”预期的返回类型“宽”的?不。方法签名必须按字面匹配。围棋没有协变或逆变的概念。

繁花如伊

Go的类型系统使用类型名称匹配。即使使用不同的类型名称返回相同的接口,此操作也会失败。但是,您可以声明该方法以满足接口,但返回可以类型断言的更广泛的实现。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go