Go语言的方法可以看成是特殊的函数,一个方法就是一个包含接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针(贯穿整个Go语言的是结构体,方法,接口的使用,可以发现每一个包都会存在结构体,对应类型的方法以及相关的接口,三者的使用可以实现很复杂的功能)。
一. 方法定义
语法规则:func (recevier type) methodName (参数列表) (返回值列表){},参数列表与返回值列表可以省略,方法比函数多了接受者这个东西:
package main
import "fmt"
type Student struct {
id int
name string
}
// 无参数列表与返回值
func (stu Student) method1() {
}
// 有参数列表无返回值
func (stu Student) method2(id int, name string) {
}
// 有参数列表有返回值
func (stu Student) method3(id int, name string) int {
return 0
}
// 有参数列表并且多个返回值
func (stu Student) method4(id int, name string) (a, b int) {
return 0, 0
}
// 返回一个结构体指针
func (stu *Student) method5(id int, name string) *Student {
stu.id = id
stu.name = name
return stu
}
func (stu Student) showMessage() {
fmt.Println(stu.id, " ", stu.name)
}
func main() {
stu1 := Student{1, "xiaozhang"}
// 值类型调用方法
stu1.showMessage()
// 指针类型调用方法
stu2 := &Student{2, "xiaohe"}
stu2.showMessage()
}
二. 方法与函数的区别
1. 对于函数来说,参数为值类型的时候不能够传递指针类型的参数,反正也成立;
2. 对于方法来说,接受者类型为值类型的时候可以使用指针类型调用方法,但是方法内部还是对副本进行操作即使是使用了指针类型调用方法,接受者为指针类型的时候可以使用值类型调用方法,但是方法内部也是对指针进行操作即使使用了值类型调用方法,编译器自动帮助我们做了取地址的操作:
package main
import "fmt"
// 函数
func funcByValueCall(a int) int {
return a + 10
}
func funcByPonitCall(a *int) int {
return *a + 10
}
// 方法
type User struct {
id int
name string
}
func (user User) methodByValueCall() {
fmt.Println(user.id, " ", user.name)
}
func (user *User) methodByPointCall(id int, name string) *User {
user.id = id
user.name = name
// 输出结构体的地址
fmt.Printf("%p", user)
return user
}
func main() {
a := 5
// 函数调用
funcByValueCall(a)
funcByPonitCall(&a)
// 方法调用
user := User{1, "xiaozhang"}
user.methodByValueCall()
(&user).methodByValueCall()
res1 := user.methodByPointCall(2, "小何")
fmt.Println(res1.id, " ", res1.name)
res2 := (&user).methodByPointCall(3, "小钱")
fmt.Println(res2.id, " ", res2.name)
}
输出结果:
1 xiaozhang
1 xiaozhang
0xc0000960602 小何
0xc0000960603 小钱
三. 方法集
每一个类型都有与之关联的方法集,定义的方法集将会影响到接口的实现,但是需要注意的是用实例值类型调用和指针类型调用方法 (含匿名字段) 不受方法集约束,编译器总是查找全部方法,并自动转换接受者的实参,方法集有如下的规则:
1. 类型 T 方法集包含全部 receiver T 方法;
2. 类型 *T 方法集包含全部 receiver T + *T 方法;
3. 类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法;
4. 类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法;
5. 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法