为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
桃花长相依 2022-09-05 15:28:20
在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 maintype (    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.GetHeightfunc 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
查看完整描述

3 回答

?
蝴蝶不菲

TA贡献1810条经验 获得超4个赞

这些类型的问题通常是以下结果:


过早的接口

假界面,或“气泡包装”(请参阅我的咆哮 这里)

并行接口

您遇到的错误与第 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()

向中添加一个新方法,该方法返回更广泛的类型,使现有方法具有相同的签名BirdMove

func (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()

这些都可以工作,这主要是偏好问题。


查看完整回答
反对 回复 2022-09-05
?
大话西游666

TA贡献1817条经验 获得超14个赞

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

不。方法签名必须按字面匹配。围棋没有协变或逆变的概念。


查看完整回答
反对 回复 2022-09-05
?
繁花如伊

TA贡献2012条经验 获得超12个赞

Go的类型系统使用类型名称匹配。即使使用不同的类型名称返回相同的接口,此操作也会失败。

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


查看完整回答
反对 回复 2022-09-05
  • 3 回答
  • 0 关注
  • 75 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信