开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情
方法与函数
方法就是一类带特殊接收者参数的函数。
方法接收者在它自己的参数列表内,位于 func 关键字和方法名之间。
比如
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
其中(v Vertex) 就是接收者参数
方法的调用:接收者.方法名(方法参数)
v := Vertex{3, 4}
v.Abs()
方法属于函数,方法只是个带接收者参数的函数 方法也可以写成正常的函数形式
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
调用时当普通函数使用
Abs(v)
只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括int之类的内建类型)的接收者声明方法;
也就是说,接收者的类型定义和方法声明必须在同一包内;
如果要使用int等内建类型当接收者,可以使用type MyFloat float64 创建float64的“别名”
指针接收者
方法接收者可以是指针,这样会修改变量值
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
// 调用
v := Vertex{3, 4}
v.Scale(10)
如果将指针接收者的方法写成普通函数的形式,要注意参数类型的匹配;函数参数是指针,则传参要传指针(&v);函数参数是值,则传参要传值(v);
// 指针参数
func ScaleFunc(v *Vertex, f float64) {
// ...
}
v := Vertex{3, 4}
ScaleFunc(&v, 10)
// 值参数
func ScaleFunc(v Vertex, f float64) {
// ...
}
v := Vertex{3, 4}
ScaleFunc(v, 10)
以指针为接收者的方法没有这样的顾虑,接收者既能为值,又能为指针; Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)。
func (v *Vertex) Scale(f float64) {
// ...
}
v := Vertex{3, 4}
v.Scale(2)
p := &Vertex{4, 3}
p.Scale(3)
反过来也是,调用以值为接收者的方法时,两种调用都可以,p.Scale(3) 会被解释为 (*p).Scale(3);
func (v Vertex) Scale(f float64) {
// ...
}
v := Vertex{3, 4}
v.Scale(2)
p := &Vertex{4, 3}
p.Scale(3)
接口
接口类型 是由一组方法签名定义的集合
type Abser interface {
Abs() float64
}
接口的实现(隐式实现):类型通过实现一个接口的所有方法来实现该接口
type I interface {
M()
}
type T struct {
S string
}
// 此方法表示类型 T 实现了接口 I
func (t T) M() {
fmt.Println(t.S)
}
接口也是值,也可以传递
func describe(i I) {
// 输出值和类型
fmt.Printf("(%v, %T)\n", i, i)
}
空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)