Golang中对方法和函数进行了区分,函数就是传统意义上的函数,定义方式如下:
func 函数名(参数列表) (返回值列表) {
}
方法在函数的基础上添加了一个接收者,接收者可以是结构体类型的变量或指针,也可以是slice或者map,也就是说,无法将基本类型的变量或指针作为方法的接收者,只有复合类型的变量或指针才可以作为接收者,定义方法的方式如下:
func (接收者) 方法名(参数列表) (返回值列表) {
}
方法的接收者可以是结构体变量或者指针,但是两者有什么区别呢?很简单,变量接收者无法改变接收者的值,而指针接收者可以修改接收者内部的值。下面我们看一个例子。
type Person struct {
Name string
}
func (p *Person) PrintMe() {
fmt.Println(p.Name)
}
func (p Person) NameToUpper1() {
p.Name = strings.ToUpper(p.Name)
}
func (p Person) NameToUpper2() string {
return strings.ToUpper(p.Name)
}
func (p *Person) NameToUpper3() {
p.Name = strings.ToUpper(p.Name)
}
我们定义了一个Person类,其中有一个属性Name,字符串类型,下面定义了几个方法,PrintMe打印名称,NameToUpper1使用变量接收者,NameToUpper2同样使用变量接收者,不过把转换为大写后的字符串返回,NameToUpper3则使用指针接收者,main函数中的调用如下:
person := Person{Name: "yjp"}
person.PrintMe()
person.NameToUpper1()
person.PrintMe()
person.NameToUpper2()
person.PrintMe()
person.Name = person.NameToUpper2()
person.PrintMe()
person.Name = "yjp"
person.NameToUpper3()
person.PrintMe()
输出为:
yjp
yjp
yjp
YJP
YJP
使用NameToUpper1我们无法修改person变量的Name属性,NameToUpper2虽然无法修改Name,但是可以返回值,还是可以将就的,最好的还是NameToUpper3,直接修改了Name属性。另外,调用NameToUpper3时,可以不使用person变量的指针,类型会进行自动转换。究其原因,使用变量作为接收者,实际是将原始变量进行了深拷贝作为了接收者,指针接收者则是将原变量的地址进行传参。如果我们的结构体比较大,还是建议使用指针作为接收者,防止每次调用方法都要进行深拷贝,影响性能。
对于slice和map类型的接收者,默认当做指针处理。看下面的例子:
type Values []int
func (values Values) PrintValues() {
fmt.Println(values)
}
func (values Values) AddOne() {
for i, v := range values {
values[i] = v + 1
}
}
type Dict map[string]int
func (d Dict) PrintDict() {
fmt.Println(d)
}
func (d Dict) AddOneByKey(key string) {
d[key] = d[key] + 1
}
逻辑很简单,自己看看,main函数中调用
values := Values{1, 2, 3}
values.AddOne()
values.PrintValues()
dict := Dict{"yjp": 1}
dict.AddOneByKey("yjp")
dict.PrintDict()
输出为
[2 3 4]
map[yjp:2]
可见,使用slice和map时,默认传递的就是slice和map的指针,我们可以直接修改其中的值。