go语言中的方法和函数的区别就是方法多了一个传值,例如:
函数:
func notify(){
fmt.Println("HelloWord")
}
方法:
func (u *user) notify(){
fmt.Println(u.name)
}
首先要明确两个定义,一个就是方法定义时选择的接收者,也就是(u *user),可以是值接收者,
也可以是指针接收者;另一个定义就是你在使用方法时传入的值是指针还是值。
在通常情况下,go会在执行的时候尽量减少错误,比如:
1、当方法是值接收者,你传的值是指针的时候
type n interface {
notify()
}
type user struct {
name string
}
func (u user) notify(){
fmt.Println(u.name)
}
func main(){
u := &user{"bill"}
u.notify()
}
这时能正常执行,因为go在代码执行的背后转为 (*u).notify(),这是go编辑器为了支持这种方法
调用背后做的事情,指针被解引用为值,这样就符合了值接收者的要求,notify操作的是一个副本,
只不过这次操作的是从u指针指向的值的副本。
2、当方法是指针接收者,你传的值是值的时侯(重复代码不再写,如上):
func (u *user) notify(){
fmt.Println(u.name)
}
func main(){
u := user{"bill"}
u.notify()
}
这时能正常执行,因为go在代码执行的背后转为 (&u).notify(),go首先引用u值得到了
一个指针,这个指针再匹配方法的接收者类型进行调用。
以上是一种正常的情况,但是并不是所有的情况go都能进行转化,这是就出现了方法集。
规范描述的方法集:
Values(你传入的值):T(值类型) MR(方法定义的接收者类型):T(值类型)
Values(你传入的值):*T(指针类型) MR(方法定义的接收者类型):T(值类型) 和 *T(指针类型)
如果上面看的别扭的话,可以从接收者类型的角度来看:
MR(方法定义的接收者类型):T(值类型) Values(你传入的值):T(值类型)和 *T(指针类型)
MR(方法定义的接收者类型):*T(指针类型) Values(你传入的值):*T(指针类型)
也就是说方法定义的时候是值接受者类型,在用的时候就可以传入值类型和指针类型,
如果方法定义的时候是指针类型,在用的时候就只能是指针类型,因为有的情况go并不能获得指针,比如:
1、
type n interface {
notify()
}
type user struct {
name string
}
func (u *user) notify(){
fmt.Println(u.name)
}
func main(){
u := user{"bill"}
s(u) //重点
}
func s(n n){
n.notify()
}
这段程序在编译器会直接报错,s(u) 这行错误,因为方法定义的是指针接收者,这样传进s函数的就是
一个复制后的值,go不能再找到原来的值的地址,就会报错,可以改为s(&u),就正确了。
2、
type n interface {
notify()
}
type user struct {
name string
}
func (u *user) notify(){
fmt.Println(u.name)
}
func main(){
user{"qew"}.notify()
}
这段代码中,main函数中,go也找不到地址,也会报错,所以方法集定义的规则在哪种情况下都不会出错,
也就是强调了指针接收者的情况下,传入的值必须是地址。