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 方法