方法声明#
在函数声明时,在其名字之前放上一个变量,即是一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import (
"fmt"
)
type Point struct{
x, y float64
}
func (p Point) dist(q Point)float64{
dx := p.x - q.x
dy := p.y - q.y
return dx*dx + dy*dy
}
func main(){
var p Point = Point{x: 1, y: 2}
var q Point = Point{x: 3, y: 4}
fmt.Println(p.dist(q))
}
|
- 其中
(p Point)
的p
被称为reciver
, reciver
只可以是类型或指针类型
- 命名建议:
reciver
取类型的第一个字母的小写
- 一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数
- 在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的(意味着P只能是一个非指针类型)
1
2
|
type P *int
func (P) f() { /* ... */ } // compile error: invalid receiver type
|
基于指针对象的方法#
1
2
3
4
|
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type Point struct{
x, y float64
}
func (p *Point) dist(q Point)float64{
dx := p.x - q.x
dy := p.y - q.y
return dx*dx + dy*dy
}
func main(){
var p Point = Point{x: 1, y: 2}
var q Point = Point{x: 3, y: 4}
fmt.Println(p.dist(q))
}
|
- 其中
p.dist(q)
编译器会隐式地帮我们用&p
去掉用dist()
方法
- 同理,如果
reciver
实参是类型T,但形参是类型*T
, 编译器会隐式的解引用
- 不管method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会做隐式转换(取引用和解引用)
Nil也是一个合法的接收器类型#
Nil也是一个合法的接收器类型,但注意nil需要是某个类型的nil。举一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package url
type Values map[string][]string
func (v Values) Get(key string) string {
if vs := v[key]; len(vs) > 0 {
return vs[0]
}
return ""
}
func (v Values) Add(key, value string) {
v[key] = append(v[key], value)
}
m := url.Values{"lang": {"en"}} // direct construction
m.Add("item", "1")
m.Add("item", "2")
fmt.Println(m.Get("lang")) // "en"
fmt.Println(m.Get("q")) // ""
fmt.Println(m.Get("item")) // "1" (first value)
fmt.Println(m["item"]) // "[1 2]" (direct map access)
m = nil // 注意这一行,m的类型是url.Values
fmt.Println(m.Get("item")) // ""
m.Add("item", "3") // panic: assig
|
通过嵌入结构体来扩展类型#
- 通过组合的方式,把组合进来的成员x的方法x_method_1()和x_method_2()变为自己的方法。
- 表现上像通过组合然后unpack的方式实现继承,或者说像mixin…
- 表现为定义一个匿名成员,如下面的
ColoredPoint
中的Point
- 类型中内嵌的匿名字段也可能是一个指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package main
import (
"fmt"
"image/color"
"math"
)
type Point struct{ X, Y float64 }
func (p Point) ScaleBy(size float64){
p.X *= size
p.Y *= size
}
func (p Point) dist(q Point)float64{
var dx float64 = p.X - q.X
var dy float64 = p.Y - q.Y
return math.Sqrt(dx*dx + dy*dy)
}
type ColoredPoint struct {
Point
Color color.RGBA
}
func main(){
red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
p.ScaleBy(2)
q.ScaleBy(3)
fmt.Println(p)
fmt.Println(q)
fmt.Println(p.dist(q.Point)) // 注意这里的调用
}
|
注意最后一行的调用,必须传入q.Point
方法值和方法表达式#
go可以像python一样,把方法绑定了一个实例,然后命名为一个函数。例如下面的distanceFromP
1
2
3
4
5
6
7
8
9
10
|
func (p Point) dist(q Point)float64{
var dx float64 = p.X - q.X
var dy float64 = p.Y - q.Y
return math.Sqrt(dx*dx + dy*dy)
}
q := Point{4, 6}
distanceFromP := p.dist
dst = Point.dist // 注意这里
fmt.Println(dst(p, q))
|
- 注意上面的
dst
, dst
默认绑定到第一个参数p
上
通过变量首字母大小写实现:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会