在本篇浅说一下go的方法变量和方法表达式,大家通常所说的方法变量就是struct实例获取方法对象,而方法表达式是struct类型获取方法对象, 需要传递struct实例对象作为参数。现在看这个也许会云里雾里,但可以通过举例来理清思路,下面就开始举例~
第一弹:方法变量
type Point struct{ X, Y float64 }
// Point类型的声明
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
// 这个方法的接收者是指针接收者,注意指针接收者和指针类型接收者的区别
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
p := Point{1, 2}
q := Point{4, 6}
distanceFromP := p.Distance // 方法变量
fmt.Println(distanceFromP(q)) // "5"
var origin Point // {0, 0}
fmt.Println(distanceFromP(origin)) // "2.23606797749979", 根号5
scaleP := p.ScaleBy // 方法变量
scaleP(2) // p 变成 (2, 4)
scaleP(3) // 然后是 (6, 12)
scaleP(10) // 然后是 (60, 120)
p.Distance可以赋予一个方法变量,它是一个函数。这个函数只需要提供实参而不需要提供接收者就能够调用。
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法变量会非常实用。举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个另外的函数。且这个函数操作的是一个Rocket对象r
type Rocket struct { /* ... */ }
func (r *Rocket) Launch() { /* ... */ }
r := new(Rocket)
time.AfterFunc(10 * time.Second, func() { r.Launch() })
直接用方法变量传入AfterFunc的话可以更为简短:
time.AfterFunc(10 * time.Second, r.Launch)
第二弹:方法表达式
distance := Point.Distance // 方法表达式
fmt.Println(distance(p, q)) // "5"
fmt.Printf("%T\n", distance) // "func(Point, Point) float64"
scale := (*Point).ScaleBy
scale(&p, 2)
fmt.Println(p) // "{2 4}"
fmt.Printf("%T\n", scale) // "func(*Point, float64)"
方法表达式携程写成T.f或者(*T).f,其中T是类型,方法表达式是一种函数变量,其第一个形参为原方法的接收者。
当需要用一个变量来代表多个方法中的一个,而方法都属于同一个类型时,方法变量可以帮助你调用这个值对应的方法来处理。
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 {
// 调用 path[i].Add(offset)或者path[i].Sub(offset).
path[i] = op(path[i], offset)
}
}
本文参考《go程序设计语言》