我在Golang的世界2天大,并经历了巡回演出.我忍不住注意到一种特殊性,我似乎无法通过适当的推理来达成一致.
此代码运行完美:
package main
import (
"fmt"
"math"
)
type Vertex struct{
X,Y float64
}
type Abser interface{
Abs() float64
}
func (v Vertex) Abs() float64{ //method with value receiver argument
return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
var myVer Vertex = Vertex{3,4}
var inter Abser
inter = &myVer //assigning *Vertex type to inter
fmt.Println(inter.Abs())
}
Run Code Online (Sandbox Code Playgroud)
同时,以下代码显示错误:
package main
import (
"fmt"
"math"
)
type Vertex struct{
X,Y float64
}
type Abser interface{
Abs() float64
}
func (v *Vertex) Abs() float64{ //method with pointer receiver argument
return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
var myVer Vertex = Vertex{3,4}
var inter Abser
inter = myVer //assigning Vertex type to inter
fmt.Println(inter.Abs())
}
Run Code Online (Sandbox Code Playgroud)
错误是:
interface.go:18:不能在赋值时使用myVer(类型Vertex)作为类型Abser:Vertex不实现Abser(Abs方法有指针接收器)
在到达巡回演唱会的这一部分之前,我可以理解Go的创作者已经放弃了笨重的符号
(*V).method1name()
(v)的.method2name()
因此,具有值接收器的方法可以与值和指针一起使用,反之亦然.
为什么语言在使用接口时会区分两者(值和指针)?如果相同的参考/解除引用原则可以适用于此,会不会更方便?
我希望我不会遗漏一些太明显的东西.谢谢!
“ Intro++ to Go Interfaces ”说明了这个问题:
*Vertex是一种类型。它是“指向一个Vertex”类型的指针。它是与 (non-pointer) 不同的类型Vertex。关于它是指针的部分是其类型的一部分。
您需要类型的一致性。
“ Go 中的方法、接口和嵌入类型”:
确定接口合规性的规则基于这些方法的接收者以及接口调用的方式。
以下是规范中关于编译器如何确定我们类型的值或指针是否实现接口的规则:
*T的方法集是所有带有接收者*T或T这条规则表明,如果我们用来调用特定接口方法的接口变量包含一个指针,那么具有基于值和指针的接收器的方法将满足该接口。
T由所有具有接收器类型的方法组成T。这条规则表明,如果我们用来调用特定接口方法的接口变量包含一个值,那么只有具有基于值的接收器的方法才能满足该接口。
Karrot Kake关于方法集的回答也在go wiki 中有详细说明:
接口类型的方法集就是它的接口。
存储在接口中的具体值是不可寻址的,就像
map元素不可寻址一样。
因此,当您在接口上调用方法时,它必须具有相同的接收器类型,或者必须可以直接从具体类型中辨别出来。如您所料,可以分别使用指针和值调用指针和值接收器方法。
可以使用指针值调用值接收器方法,因为它们可以首先取消引用。
但是,不能使用值调用指针接收器方法,因为存储在接口中的值没有 address。
(“没有地址”实际上意味着它不可寻址)