方法
在 Go 语言中,结构体就像是类的一种简化形式,那么面向对象程序员可能会问:类的方法在哪里呢?在 Go 语言中有一个概念,它和方法有着同样的名字,并且大体上意思相近。
Go 语言中方法和函数在形式上很像,它是作用在接收器(receiver)上的一个函数,接收器是某种类型的变量。因此方法是一种特殊类型的函数,方法只是比函数多了一个接收器(receiver),当然在接口中定义的函数我们也称为方法(因为最终还是要通过绑定到类型来实现)。
正是因为有了接收器,方法才可以作用于接收器的类型(变量)上,类似于面向对象中类的方法可以作用于类属性上。
定义方法的一般格式如下:
func (recv receiver_type) methodName(param_list) (return_value_type) {
// ...
}
在方法名之前,func 关键字之后的括号中指定接收器 receiver。
接收器
- 接收器类型除了不能是指针类型或接口类型外,可以是其他任何类型,不仅仅是结构体类型,也可以是函数类型,还可以是 int、bool、string 等等为基础的自定义类型。
- 接收器不能是一个接口类型,因为接口是一个抽象定义,但是方法却是具体实现;如果这样做会引发一个编译错误:invalid receiver type…。
- 接收器不能是一个指针类型,但是它可以是任何其他允许类型的指针。
- 接收器不能是一个指针类型,但可以是类型的指针。
方法值与方法表达式
和 JS使用函数类似,Go的方法也有使用方法赋值和方法表达式的2种方式。
方法值调用:将方法以“值”的形式传入或者赋值,然后使用。
type Rocket Struct { /*...*/ }
func (r *Rocket) Launch() {
/* ... */
}
r := new(Rocket)
time.AfterFunc(10 * time.Second, r.launch)
方法表达式:使用方法时,与调用一个普通函数相比,要用选择器语法指定方法的接收器。
f1 := T.mv
f2(t, 5)
f2 := (T).mv
f2(t, 6)
这个函数值的第一个参数必须是一个接收器。
和JS种函数的使用方式一样,可以选择将一个函数计算赋值给一个变量,也可以将一个函数表达式赋值给变量。
当你根据一个变量来决定调用同一个类型的哪个函数时,方法表达式就显得很有用了。你可以根据选择来调用接收器各不相同的方法。
type Point struct{ X, Y float64 }
func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} }
func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} }
type Path []Point
func (path Path) TranslateBy(offset Point, add bool) {
var op func(p, q Point) Point
if add {
op = Point.Add
} else {
op = Point.Sub
}
for i := range path {
// Call either path[i].Add(offset) or path[i].Sub(offset).
path[i] = op(path[i], offset)
}
}