2016-02-15 03:06:34 +00:00
|
|
|
|
## 6.4. 方法值和方法表达式
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2018-06-09 16:27:25 +00:00
|
|
|
|
我们经常选择一个方法,并且在同一个表达式里执行,比如常见的p.Distance()形式,实际上将其分成两步来执行也是可能的。p.Distance叫作“选择器”,选择器会返回一个方法“值”->一个将方法(Point.Distance)绑定到特定接收器变量的函数。这个函数可以不通过指定其接收器即可被调用;即调用时不需要指定接收器(译注:因为已经在前文中指定过了),只要传入函数的参数即可:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
p := Point{1, 2}
|
|
|
|
|
q := Point{4, 6}
|
|
|
|
|
|
2016-01-02 13:17:21 +00:00
|
|
|
|
distanceFromP := p.Distance // method value
|
|
|
|
|
fmt.Println(distanceFromP(q)) // "5"
|
|
|
|
|
var origin Point // {0, 0}
|
|
|
|
|
fmt.Println(distanceFromP(origin)) // "2.23606797749979", sqrt(5)
|
|
|
|
|
|
2015-12-09 07:45:11 +00:00
|
|
|
|
scaleP := p.ScaleBy // method value
|
|
|
|
|
scaleP(2) // p becomes (2, 4)
|
|
|
|
|
scaleP(3) // then (6, 12)
|
|
|
|
|
scaleP(10) // then (60, 120)
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-09 16:27:25 +00:00
|
|
|
|
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法“值”会非常实用(``=_=`真是绕)。举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
type Rocket struct { /* ... */ }
|
|
|
|
|
func (r *Rocket) Launch() { /* ... */ }
|
|
|
|
|
r := new(Rocket)
|
|
|
|
|
time.AfterFunc(10 * time.Second, func() { r.Launch() })
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-09 16:27:25 +00:00
|
|
|
|
直接用方法“值”传入AfterFunc的话可以更为简短:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
time.AfterFunc(10 * time.Second, r.Launch)
|
|
|
|
|
```
|
2016-01-02 13:17:21 +00:00
|
|
|
|
|
2016-02-15 03:06:34 +00:00
|
|
|
|
译注:省掉了上面那个例子里的匿名函数。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2018-06-09 16:27:25 +00:00
|
|
|
|
和方法“值”相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2018-06-09 16:27:25 +00:00
|
|
|
|
当T是一个类型时,方法表达式可能会写作`T.f`或者`(*T).f`,会返回一个函数“值”,这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
p := Point{1, 2}
|
|
|
|
|
q := Point{4, 6}
|
|
|
|
|
|
|
|
|
|
distance := Point.Distance // method expression
|
|
|
|
|
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)"
|
2016-01-02 13:17:21 +00:00
|
|
|
|
|
2016-02-15 03:06:34 +00:00
|
|
|
|
// 译注:这个Distance实际上是指定了Point对象为接收器的一个方法func (p Point) Distance(),
|
|
|
|
|
// 但通过Point.Distance得到的函数需要比实际的Distance方法多一个参数,
|
|
|
|
|
// 即其需要用第一个额外参数指定接收器,后面排列Distance方法的参数。
|
|
|
|
|
// 看起来本书中函数和方法的区别是指有没有接收器,而不像其他语言那样是指有没有返回值。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
```
|
|
|
|
|
|
2016-02-15 03:06:34 +00:00
|
|
|
|
当你根据一个变量来决定调用同一个类型的哪个函数时,方法表达式就显得很有用了。你可以根据选择来调用接收器各不相同的方法。下面的例子,变量op代表Point类型的addition或者subtraction方法,Path.TranslateBy方法会为其Path数组中的每一个Point来调用对应的方法:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
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) {
|
2016-01-21 02:08:07 +00:00
|
|
|
|
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)
|
|
|
|
|
}
|
2015-12-09 07:45:11 +00:00
|
|
|
|
}
|
|
|
|
|
```
|