Go中的方法接收器

199 阅读3分钟

接收器

在Go中,除内置类型外,我们还可以创建自定义类型,并为这些自定义类型添加对应的方法,例如:

type Myint int
type Str string
type Apple struct{}
...

func (m Myint) Prints() {...}
func (m *Myint) Add() {...}
func (s Str) Prints() {...}
... 

Go有两种方法接收器

  • 值接收器

  • 方法接收器

在上述定义的func中,诸如(s Str)(m Myint)的方法为值接收器方法,(m *Myint)对应方法是指针接收器方法。

一个自定义类型中即可以存在值接收器方法,也可以存在指针接收器方法,但Go不这么建议,Go建议每个自定义类型的方法对应的接收器要统一,要么是值接收器,要么是指针接收器。

实现接口

​ 接口有对方法的定义,自定义类型可以通过实现接口定义的方法从而来实现接口,下面以结构体为例

type Animal interface{
    Sleep()
}

type Cat struct {}

func (c Cat) Sleep() {
    fmt.Println("cat is sleeping")
}


func main() {
	var buou = Cat{}
    
    var animal Animal = buou
    animal.Sleep()
    // 输出cat is sleeping
}

​ 上面例子中定义了接口Animal,它有方法Sleep(),同时定义了Cat结构体,并实现了Sleep方法,从而实现了Animal接口;因此在main函数中可以通过定义Animal类型的animal变量,将Cat类型的buou变量赋给animal。 此时,如果为Animal添加一个新方法,同时Cat用指针接收器实现该方法,会出现什么问题呢?

type Animal interface{
    Sleep()
    Eat()
}

type Cat struct {}

func (c Cat) Sleep() {
    fmt.Println("cat is sleeping")
}

func (c *Cat) Eat() {
    fmt.Println("cat is eatting")
}

func main() {
	var buou = Cat{}
    
    var animal Animal = buou
    animal.Sleep()
    // 输出cat is sleeping
    
    animal.Eat()
    // 编译不通过
}

​ 执行go run main.go,看到终端出现以下报错提示,它表明buou这一变量没有实现Animal接口;

.\main.go:25:22: cannot use buou (variable of type Cat) 
as Animal value in variable declaration:
Cat does not implement Animal (method Eat has pointer receiver)

​ 这是因为Animal有两个方法,buouCat类型, 而Cat类型相对应的只实现了Sleep这一值接收器的方法,Eat方法是*Cat类型实现的,因此值变量不能调用指针接收器方法。让我们使用指针变量来看一下:

type Animal interface{
    Sleep()
    Eat()
}

type Cat struct {}

func (c Cat) Sleep() {
    fmt.Println("cat is sleeping")
}

func (c *Cat) Eat() {
    fmt.Println("cat is eatting")
}

func main() {
    var buou = &Cat{}

    var animal Animal = buou
    animal.Sleep()
    // 输出cat is sleeping

    animal.Eat()
    // 输出cat is eatting
}

执行go run main.go,程序正常输出,这时候大家可能会有疑惑,指针变量的buou相比于值变量,为什么指针变量能够调用值接收器方法,而值变量不能够调用指针接收器方法?

我一开始也有这个疑惑,后面查了一些资料,才发现原因是Go的编译器在编译阶段,会为程序中所有定义了值接收器方法的自定义类型再添加一个指针接收器方法,方法内容是一致的;但是不会为指针接收器方法添加值接收器的方法。例如上述代码中,编译器在编译期为Cat结构体的Sleep方法中增加了一模一样的指针接收器方法:

type Animal interface{
    Sleep()
    Eat()
}

type Cat struct {}

func (c Cat) Sleep() {
    fmt.Println("cat is sleeping")
}

// 编译阶段添加
func (c *Cat) Sleep() {
    fmt.Println("cat is sleeping")
}

// 注意:不会为Cat值接收器添加Eat方法
func (c *Cat) Eat() {
    fmt.Println("cat is eatting")
}


​ 因此指针接收器才能正常调用值接收器的方法,总结如下

值接收器方法指针接收器方法
值变量×
指针变量