Go语言没有复杂的class概念,严格来说不是面向对象的语言,她和C语言很像,是面向函数的;Go中函数用关键字func来声明。Go没有class但是有struct、interface,可以把他们理解成类型定义;问题来了,有一种用func声明的函数可以指定struct、interface类型的接收者,这种函数在Go语言中单独叫做方法;方法的接收者可以是一个类型的值也可以是一个类型的引用(指针)。

对Go语言数据类型的整体认识,可以参考以前的文章:Go学习(一)数据类型的本质

值接收者和指针接收者

  • 方法的接收者是某类型的值变量,称为值接收者方法
  • 方法的接收者是某类型的指针变量,称为指针接收者方法

下面我们来做一个实验,先定义一种鸭子(几条腿,是否会飞,是否能游泳),然后为鸭子类型声明两个方法,方法的作用一样都是设置鸭子有几条腿;只不过一个是值接收者,一个是指针接收者:

 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
type Duck struct {
	legs    int
	hasWing bool
	canSwim bool
}

// 值接收者方法
func (dk Duck) SetLegs(num int) {
	dk.legs = num
}

// 指针接收者方法
func (dk *Duck) ChangeLegs(num int) {
	dk.legs = num
	// (*dk).legs = num	
}

func ShowMeTheLegs() {
	first := Duck{}
	fmt.Println("1. First duck legs: ", first.legs)
	first.SetLegs(2)
	fmt.Println("2. First duck legs: ", first.legs)
	first.ChangeLegs(4)
	fmt.Println("3. First duck legs: ", first.legs)

	second := new(Duck)
	fmt.Println("4. Second duck legs: ", second.legs)
	second.SetLegs(4)
	fmt.Println("5. Second duck legs: ", second.legs)
	second.ChangeLegs(4)
	fmt.Println("6. Second duck legs: ", second.legs)
}

大家猜一猜执行ShowMeTheLegs()函数,打印的结果是啥?很简单,就是下面结果:

1. First duck legs:  0
2. First duck legs:  0
3. First duck legs:  4
4. Second duck legs:  0
5. Second duck legs:  0
6. Second duck legs:  4

结论

第一:变量first是一个值变量,second是一个指针变量,结果他们都可以调用值接收者和指针接收者声明的方法。

第二:值接收者方法修改的值并不改变调用者,指针接收者方法修改的值改变了调用者。

image-20201229113745458

无论是类型的值或指针,在调用指针接收者方法或值接收者方法时,编译器都对他们做了相应的转换,所以互相混着调用都能成功。之间的关系就如上图所示。

但是接口的变量却不能随意混合调用值接收者方法和指针接收者方法,这一话题我们放在以后(接口和方法集)单独讨论。

(完)