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