问题描述
我在下面介绍了两个程序:程序1和程序2。
我希望程序1无法编译,并且确实无法编译。这样很好。
我希望程序2无法编译,但可以成功!这个问题是关于程序2成功的原因。
程序1
https://play.golang.org/p/qX9nY8VLlx0
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
type Vertex struct {
X float64
Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser
a = Vertex{3,4}
fmt.Println(a.Abs())
}
无法通过此错误进行编译:
./prog.go:24:4: cannot use Vertex literal (type Vertex) as type Abser in assignment:
Vertex does not implement Abser (Abs method has pointer receiver)
我之所以会遇到此错误,是因为*Vertex
实现了Abser
,但Vertex
没有实现,因此我们无法将Vertex
对象分配给Abser
变量。 / p>
程序2
https://play.golang.org/p/4bIs-fHGhYm
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
type Vertex struct {
X float64
Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser
a = &Vertex{3,4}
fmt.Println(a.Abs())
}
这将成功编译。该程序的输出为:
5
为什么成功?在这里,Vertex
实现了Abser
,但是*Vertex
没有实现Abser
。然后如何为*Vertex
分配类型为Abser
的值?
要知道为什么成功,我需要了解哪些语言语义规则?
解决方法
assignability要求阐明了使用接口时的要求:
值x可分配给类型T的变量(“ x可分配给 T“)如果满足以下条件之一:
... T是接口类型,x实现T。 ...
要确定“ x实现T”的规则,我们转向method sets的概念:
类型可能具有与之关联的方法集。一个方法集 接口类型是它的接口。任何其他类型T的方法集 包含以接收方类型T声明的所有方法。
乍看之下,这意味着第一个示例中的Vertex
方法集为空,而第二个示例中的*Vertex
方法集也为空。
但是,规范会继续指定:
相应指针类型* T的方法集为所有 用接收者* T或T声明的方法(也就是说,它还包含 T的方法集。
这意味着{strong>方法集*Vertex
自动包含Vertex
的方法集,但反之则不包括。
这在您的第一个示例中无济于事,但是在第二个示例中,Vertex
的方法集确实包含Abs() float64
。由于这是实现Abser
接口所需的全部内容,因此*Vertex
被认为是实现Abser
的地方。