一、在Golang中,函数和方法是不一样的,函数没有接收者,而方法有接收者,接收者是某个结构体类型的变量。
其中方法的接受者有两种:
value receiver(值接收者,只能获取结构体变量的属性),
pointer receiver(指针接收者,还可以通过方法参数改变结构体变量的属性)
  方法:(接收者可以理解为调用该方法的结构体实例,即调用者)
  1. 值接受者(和值传递概念相同,只是值拷贝给接收者参数)
    func (valueReceiver receiverType) funcName(params type) returnValues type{
    }

  2. 指针接收者(接收者是结构体类型变量指针,即变量的地址,所以可以改变变量的属性值)
    func (pointerReceiver *receiverType) funcName(params type) returnValues type{
    }

    注:方法常用在实现接口方法时。

  函数:

       func funcName(params type) returnValues type{
       }

有地方解释:pointer receiver的方法接收者既可以传值也可以传指针,事实上pointer receiver的方法只是将receiver.funcName()自动转换为(&receiver).funcName(),同样地,把value receiver 指向变量。这点官方的go有说明,go编译器自动取了变量的地址。

下面通过几个例子说明区别和细节:

package main

import "fmt"

type Stu struct {
}

func (s Stu) pt1(str string){ //值接受者
	fmt.Println(str)
}
func (s *Stu) pt2(str string){	//指针接受者
	fmt.Println("func pt2 ",s)
	fmt.Println(str)
}
func main() {
	s1 := new(Stu) //指针
	s1.pt1("s1")	//	s1
	fmt.Println(s1)	//	&{}

	s2 := Stu{}		//值
	s2.pt2("s2")	//	s2
	fmt.Println(s2)	//	{}
	
	//说明go编译器会自动指向方法接收者 和 自动取接收者地址
}

在实现接口的方法中,value receivers和pointer receivers就很明显的体现了。

二、接口的实现

  下面通过两个例子说明区别和细节:

1.当实现接口的方法时,实现方法中有指针接收者的方法时,只能用指针变量实现接口。
package main
import "fmt"

type People interface { //Person接口 只有一个方抽象方法 Speak()
	Speak(string)
	Eat(string)
}

type Student struct{//结构体类型 Student 只有一个name 成员属性
	name string
}

//方法 (实现方法) 这是一个指针接收者(pointer receiver)
func (stu *Student) Speak(words string) {
	name := stu.name
	fmt.Println(name,"说:",words)
}
//方法 (实现方法) 这是一个值接收者(value receiver)
func (stu Student) Eat(foods string) {
	name := stu.name
	fmt.Println(name,"吃了:",foods)
}
//实现接口有两个步骤:
//1.以上 结构体类型 Student 实现了接口的全部方法,在golang中也就是隐式实现了接口Person
//2.实例在声明的时还需要显式转换为接口类型 eg: var people People = new(Student)
func main() {
	
	var stuValue Student // 这是一个stu 变量
	stuValue.name = "张三"

	stuPointer := new(Student) // 这是一个stu 指针变量 (指针变量一定要用new 关键字分配内存空间 或者取变量地址获得)
	stuPointer.name = "李四"

	var people1 People = stuValue
	// 报错:does not implement People as Speak method has a pointer receiver
	//必须是一个pointer receiver 否则不能实现接口

	var people2 People = stuPointer //通过实现
	//成功调用两种实现方法
	people2.Speak("hello!") //Speak 方法定义的是指针接收者,可以正常调用
	people2.Eat("香蕉") //Eat 方法定义的是值接收者,此时也可以正常调用(编译器会通过指针自动指向变量)
	fmt.Println(people2)  //&{李四}

}
2.当实现接口的方法时,实现方法中没有指针接收者的方法,指针变量和值变量都可以实现该接口。
package main
import "fmt"

type People interface { //Person接口 只有一个方抽象方法 Speak()
	Speak(string)
	Eat(string)
}

type Student struct{//结构体类型 Student 只有一个name 成员属性
	name string
}

//方法 (实现方法) 这两个实现方法都是 值接收者(value receiver)
func (stu Student) Speak(words string) {
	name := stu.name
	fmt.Println(name,"说:",words)
}
func (stu Student) Eat(foods string) {
	name := stu.name
	fmt.Println(name,"吃了:",foods)
}
//实现接口有两个步骤:
//1.以上 结构体类型 Student 实现了接口的全部方法,在golang中也就是隐式实现了接口Person
//2.实例在声明的时还需要显式转换为接口类型 eg: var people People = new(Student)
func main() {
	var stuValue Student // 这是一个stu 变量
	stuValue.name = "张三"
	stuPointer := new(Student) // 这是一个stu 指针变量 (指针变量一定要用new 关键字分配内存空间 或者取变量地址获得)
	stuPointer.name = "李四"
	
	//实现方法中没有指针接收者的方法,指针变量和值变量都可以实现该接口。并成功调用接口方法。
	var people1 People = stuValue
	people1.Eat("西瓜!") //Eat 方法定义的是值接收者,此时值接收者也可以正常调用
	people1.Speak("你好") //Speak 方法定义的是指针接收者,可以正常调用
	fmt.Println(people1)  //&{李三}

	var people2 People = stuPointer //通过
	people2.Eat("香蕉") 
	people2.Speak("hello!")
	fmt.Println(people2)  //&{李四}
}

总结:
1.在使用方法时尽量定义为指针接收者(pointer receiver)
func (pointerReceiver *receiverType) funcName(params type) returnValues type{
}
2.而在实现接口时:
var people People = new(Student)显式实现用指针变量