Go语言中的方法是一种特殊类型的函数。
Go语言中的方法声明和函数声明相似,不同之处在于声明时方法比函数多了一个receiver(接收者)部分。在receiver部分声明的参数,Go称之为receiver参数,这个receiver参数也是方法与类型之间的纽带。Go中的方法必须归属于一个类型,或者说这个方法就是这个类型的方法.。
方法声明要与receiver参数的基类型声明放在同一个包内,基于这个约束可以得到两个推论:
- 我们不能为原生类型(如int、string、map等)添加方法
- 不能跨越Go包为其他包的类型声明新的方法
方法定义
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
值接收者
在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。 原因是当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。
package main
import "fmt"
type User struct {
name string
age int
}
//值作为接收者,可以访问对象成员
func (u User) GetName() string {
return u.name
}
//值作为接收者, 无法修改对象成员
func (u User) SetName(name string) {
u.name = name
}
func main() {
user := User{name: "zhangsan"}
fmt.Println(user.GetName()) // zhangsan
user.SetName("lisi")
fmt.Println(user.GetName()) // zhangsan
}
指针接收者
在指针类型接收者的方法中也可以获取接收者的成员值,而且可以修改接收者变量本身。
package main
import "fmt"
type User struct {
name string
age int
}
// 指针作为接收者,也可以访问对象成员
func (u *User) GetName() string {
return u.name
}
// 指针作为接收者,可以修改对象成员
func (u *User) SetName(name string) {
u.name = name
}
func main() {
user := User{name: "zhangsan"}
fmt.Println(user.GetName()) // zhangsan
user.SetName("lisi")
fmt.Println(user.GetName()) // lisi
}
接收者类型的选择
通过前面两个例子可以看到,接收者参数类型有值类型和指针类型,那么在方法定义时应该选择哪个类型合适呢?
原则一:如果要在方法中对类型实例进行修改,比如上例中的SetName方法,那么我们应该选择指针类型作为接收者参数的类型,也就是上例中的(u *User)。
原则二:如果不需要在方法中对类型实例进行修改,这个时候接收者参数选择值类型,也就是上例中的(u User)。因为这样可以尽量少暴露可以修改类型内部状态的方法。 不过有个例外:如果接收者参数类型的size较大,这时选择指针类型作为接收者类型更好。因为Go方法调用时,接收者参数是以值拷贝的形式传入方法中的。