在《A Go Tour》中,我如何修改接口以要求在指针上定义方法?

huangapple go评论82阅读模式
英文:

In "A Go Tour", how do I modify the interface to require a method on a pointer?

问题

在《A Go Tour》中的接口部分,我们有以下接口:

type Abser interface {
    Abs() float64
}

解释说,这个类型Vertex不满足上述的Abser接口:

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

我应该如何修改Abser接口,使得Vertex满足它?

(如果在完整示例中不再满足MyFloat也没关系。)

在发布这个问题之前,我花了几个小时搜索和思考,但在发布后,我发现了另一个类似的问题,它涵盖了类似的内容,可能对其他初学者有帮助:

《Go编程语言中的“方法需要指针接收者”》

英文:

In "A Go Tour" on Interfaces we have this interface:

type Abser interface {
	Abs() float64
}

It is explained that this type, Vertex, does not satisfy the above Abser:

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

How do I modify the Abser interface so that Vertex satisfies it?

(It's okay if it no longer satisfies MyFloat in the full example.)

Before I posted this question I spent some hours searching and head scratching but after posting I found another SO question which covers similar material and may be of help to other neophytes:

"method requires pointer receiver” in Go Programming Language

答案1

得分: 2

你对实现关系有些困惑。在这种情况下,*Vertex 实现了 Abser 接口,而不是 Vertex,如果你想让 Vertex 也实现该接口,你需要在 Abs 的实现中改变接收类型,使其不是指针类型:

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
} // 这里是 Vertex 实现 Abser

所以你提出的问题有点误导。接口不需要任何更改,也不应该有任何更改。只是提醒一下,你可以通过使用我上面提到的方法以及你已经有的方法,让 *VertexVertex 都实现该接口。

这是 Go 中经常让人困惑的一点。当你声明 func (t SomeType) 时,你指定了接收类型,如果这是一个指针,比如 (t *SomeType),那么实现接口的是 *SomeType 而不是 SomeType。这与其他语言有些不同,其他语言中方法是由类实现的,无论你使用引用还是值都没有关系。在 Go 中,类并不实现方法,你指定的是接收类型,即方法可以在其上调用的类型。第一个参数实际上被推送到堆栈上,并像传递给方法的参数一样使用,这种区别只在编译时有意义(我认为实现接口是唯一需要此功能的原因)。所以,总结一下,如果你想让 Vertex 实现 Abser 接口,该接口包含一个方法 Abs() float64,那么你需要将函数定义为 func (v Vertex) Abs() float64 { ... }

出于代码清晰可读的考虑,我建议不要同时为指针和值定义接口...你可以使用取地址操作符或解引用操作符来解决这个问题。

英文:

You're slightly confused about what is implementing what. In this case, *Vertex is implementing Abser and not Vertex to make it so Vertex does as well, you have to change the receiving type on your implementation of Abs so that it's not a pointer;

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
} // Vertex implementing Abser here

So the way you're phrasing the question is a bit misleading. The interface requires no change and should receive none. Just fyi, you can have both *Vertex and Vertex implement the interface by using the method I put above as well as the one you already have.

This is a point that often trips people up in Go. When you declare func (t SomeType) you're specifying the receiving type, if this is a pointer like (t *SomeType) then the thing implementing the interface is not SomeType but rather *SomeType. This differs a little from other languages where the method is implemented by the class and it doesn't matter if you're working with a reference or a value. In Go, the class doesn't implement the method, you specify the 'receiving type' - the thing that the method can be called on - that first argument actually gets pushed on the stack and used the same as an argument passed into a method, the distinction is only relevant for compilation (interface fulfillment really is the only reason to have the feature afaik). So, to summarize, if you want Vertex to implement the Abser interface which features a single method Abs() float64 then you need to define the function as func (v Vertex) Abs() float64 { ... }.

Just for the sake of having clear readable code I recommend not having the interface defined for both pointers and values... You can work around that using the address of or dereference operator.

huangapple
  • 本文由 发表于 2015年10月21日 04:11:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/33245947.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定