Go 方法和接口
方法
Go 没有类。不过你可以为结构体类型定义方法。
方法就是一类带特殊的 接收者 参数的函数。
方法接收者在它自己的参数列表内,位于 func
关键字和方法名之间。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{
3, 4}
fmt.Println(v.Abs())
}
输出:
5
在此例中,Abs
方法拥有一个名为 v
,类型为 Vertex
的接收者。
方法即函数
记住:方法只是个带接收者参数的函数。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{
3, 4}
fmt.Println(Abs(v))
}
输出:
5
这个例子中 Abs
的写法就是个正常的函数,功能并没有什么变化。
方法(续)
你也可以为非结构体类型声明方法。
package main
import (
"fmt"
"math"
)
type MyFloat float64
func (f MyFloat) Abs() MyFloat {
if f < 0 {
return -f
}
return f
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
输出:
1.4142135623730951
在此例中,我们看到了一个带 Abs
方法的数值类型 MyFloat
。
你只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int
之类的内建类型)的接收者声明方法。
(译注:就是接收者的类型定义和方法声明必须在同一包内;不能为其他包中声明的类型声明方法。)
指针接收者
你可以为指针接收者声明方法。
这意味着对于某类型 T
,接收者的类型可以用 *T
的文法。(此外,T
不能是像 *int
这样的指针。)
例如,这里为 *Vertex
定义了 Scale
方法。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{
3, 4}
v.Scale(10)
fmt.Println(v.Abs())
}
输出:
50
指针接收者的方法可以修改接收者指向的值(就像 Scale
在这做的)。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
试着移除第 16 行 Scale
函数声明中的 *
,观察此程序的行为如何变化。
若使用值接收者,那么 Scale
方法会对原始 Vertex
值的副本进行操作。(对于函数的其它参数也是如此。)Scale
方法必须用指针接受者来更改 main
函数中声明的 Vertex
的值。
指针与函数
现在我们要把 Abs
和 Scale
方法重写为函数。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func Scale(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{
3, 4}
Scale(&v, 10)
fmt.Println(Abs(v))
}
输出:
50
同样,我们先试着移除掉第 16 行 Scale
函数声明中的 *
。尝试编译,会得到 ./src.go:23:8: cannot use &v (type *Vertex) as type Vertex in argument to Scale
,按照提示,我们再把 main
函数中 Scale
函数的调用里的 &v
改成 v
,再尝试编译,通过了,输出结果为 5
。
方法与指针重定向
package main
import "fmt"
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X *= f
v.Y *= f
}
func ScaleFunc(v *Vertex, f float64) {
v.X *= f
v.Y *= f
}
func main() {
v := Vertex{
3, 4}
v.Scale(2)
ScaleFunc(&v, 10)
p := &Vertex{
4, 3}
p.Scale(3)
ScaleFunc(p, 8)
fmt.Println(v, p)
}
输出&#