Go语言中的方法
方法用于给指定数据类型(非内置数据类型)添加新的行为,方法实质上也是函数,与函数不同的是,每个方法包含了一个方法的作用对象参数,即:接收者。定义方法的语法为:
package main
import "fmt"
type user struct{
id int
name string
}
//方法的定义: 接收者位于func关键字和方法名之间
func (u user) GetName() string {
return u.name
}
func main() {
u := user{0, "John"}
u.GetName() //方法的调用
}
为什么说方法实质上也是函数呢?因为它与下面的函数是等价的:
func GetName(u user) string {
return u.name
}
方法的接收者
根据方法的语法,介于func
关键字和函数名之间的参数即为方法的接收者。
接收者的类型可以是值类型,也可以是指针类型:
func (u user) GetName() string {
return u.name
}
func (u *user) GetId() int {
return u.id
}
接收者实质上也是作为一个参数传递给方法,Go语言中的参数传递方式都是值传递,所以:
- 在值类型接收者的方法中,在方法调用时会使用接收者值的一个副本来执行。
- 在指针类型的接收者方法中,在方法调用时会使用指针的一个副本来执行。
对于值类型的接收者方法来说,可以用指针来调用,反之亦然:
var u1 = &user{1, "Alice"}
u1.GetName() //执行时相当于: (*u1).GetName()
var u2 = user{2, "John"}
u2.GetId() //执行时相当于: (&u2).GetId()
如何选择接收者的类型
通常情况下,都将接收者设为指针类型。因为接收者为指针类型,所以对于状态的变更会直接影响到接收者,如果是值类型的接收器的话,则接收者不会受影响。
func (u *user) SetId(id int) {
u.id = id
}
func (u user) SetName(name string) {
u.name = name
}
var u user
u.SetId(1) //变量u的id会被更改
u.SetName("Tracy") //变量u的name不会被更改
从上面可以看出,如果需要对接收者做更改,需要使用指针类型的接收者。但不做更改的话是不是就可以使用值类型呢?如果是没有状态维护的结构体则可以使用值类型的接收者,如果结构体体积比较大,考虑到副本的拷贝效率,仍然建议使用指针类型的接收者。
type matcher struct{}
func (m matcher) Match(args ...interface) {
//因为没有状态维护,所以可以使用值接收者
}
对于别名类型,也同样适用:
type IntArray []int
func (ia IntArray) Len() int {
return len(ia)
}
func (ia *IntArray) Del(target int) {
for i, v := range *ia {
if v == target {
(*ia) = append((*ia)[:i], (*ia)[i+1:]...)
break
}
}
}
总结
Go语言中的方法可能会对其他语言的开发者来说有点不习惯,但方法的本质就是一个函数,接收者是该函数的第一个参数,从这个角度出发,该用指针接收者还是值接收者,就比较明确了。
欢迎指正!
原文链接:Go语言如何选择方法接收器的类型
网友评论