Golang:方法与接口

55 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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)
}

空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)