接收者有两种,一种是值接收者,一种是指针接收者。顾名思义,值接收者,是接收者的类型是一个值,是一个副本,方法内部无法对其真正的接收者做更改;指针接收者,接收者的类型是一个指针,是接收者的引用,对这个引用的修改之间影响真正的接收者。下面看一个最基本的例子。
package main
type foo struct {
val int
}
// 需要改变成员变量的都定义为指针接收者
func (f *foo) changeVal(newVal int) {
f.val = newVal
}
func (f foo) getVal() int {
return f.val
}
func main() {
fo := &foo{1}
println(fo.getVal()) // 1
println("----------------------------")
fo.changeVal(100)
println(fo.getVal()) // 100
}
运行结果
1
----------------------------
100
如果把changeVal改成值接收者会怎样?
func (f foo) changeVal(newVal int) {
f.val = newVal
}
结果值并没有改变
1
----------------------------
1
把fo现在是一个结构的地址,如果把它改为值会怎么样?
// fo := &foo{1} 换为
fo := foo{1}
结果还是能改变值
1
----------------------------
100
-
不论fo是指还是指针,两个方法都能正常调用,实际上在编译的时候系统会进行隐式转换,根据函数的定义选择合适的实际接收者。
-
用指针作为接收者定义的方法才能改变struct中的属性,值接收的改变不了,相当于C++中的const成员函数,只是C++中const成员函数试图改变成员变量时编译器会报错,而Go不会提示。
再看一个复杂一些的例子,interface{}数组如何?
package main
type bar interface {
changeVal() //这个函数改变struct 内的成员变量
getVal() int
}
type foo struct {
val int
}
// 需要改变成员变量的都定义为指针接收者
func (f *foo) changeVal() {
f.val = 100
}
// 承诺不改变成员变量的都定义为值接收者
func (f foo) getVal() int {
return f.val
}
func main() {
f := make([]bar, 0, 100)
for i := 0; i < 3; i++ {
f = append(f, &foo{1})
}
for _, fo := range f {
println(fo.getVal())
fo.changeVal()
println(fo.getVal())
println("----------------------------")
}
println("++++++++++++++++++++++++++++++++")
for _, fo := range f {
println(fo.getVal())
}
}
运行结果
1
100
----------------------------
1
100
----------------------------
1
100
----------------------------
++++++++++++++++++++++++++++++++
100
100
100
如果把25行传入的结构的地址换成值会如何呢,答案是编译不通过,提示 foo{1} 没有完全实现 bar 这个接口。
这就很奇怪了,前面单个值调用指针定义的方法成功了啊?实际上这就是编译器的问题,在单个值的时候,编译器会自动隐式转换,找到相应的地址。但是本质上foo类型并不算实现了bar接口
再看一个例子
type bar interface {
changeVal() //这个函数改变struct 内的成员变量
getVal() int
}
type foo struct {
val int
}
// 需要改变成员变量的都定义为指针接收者
func (f *foo) changeVal() {
f.val = 100
}
// 承诺不改变成员变量的都定义为值接收者
func (f foo) getVal() int {
return f.val
}
func changeAndOutPut(b bar){
b.changeVal()
fmt.Println(b.getVal)
}
func main(){
f := foo{1}
changeAndOutPut(f) // 提示报错,f没有实现interface bar
}