go语言方法集
规则
先介绍一下go语言的方法集规则:
意思是:如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。
示例
光说理论可能比较难理解,看代码:
package main
import "fmt"
type notifyInterface interface {
notify()
}
func sendEmail(n notifyInterface) {
n.notify()
}
type user struct {
name string
email string
}
func (u *user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
func main() {
u1 := user{
name: "john smith",
email: "john@yahoo.com",
}
sendEmail(u1)
}
这个代码是有问题的,报错如下:
.\main.go:29:12: cannot use u1 (variable of type user) as type notifyInterface in argument to sendEmail:
user does not implement notifyInterface (notify method has pointer receiver)
原因很简单,因为违反了go语言的方法集规则,值类型的user
没有实现notify
方法(notify:是你的指针实现的我,我和你不熟,快爬快爬)
但是如果反过来,把notify
的接受者改成值类型:
func (u user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
那么无论是指针类型,或是值类型的user
都能接受。好像值类型就天生比指针类型高级一点(可以这样辅助记忆)。
问题
那么问题来了,为什么会有这么看上去不”优雅“的设计呢?凭什么值类型天生比指针类型高级?我要打拳了(不是)!
书上是这么解释的:编译器并不是总能自动获得一个值的地址
那么问题又来了,什么时候编译器可以自动获取一个值的地址?
根据我目前的学习结果,答案是 当值为方法的调用者时
go语言编译器为方法调用者提供了非常甜的语法糖:
type user struct {
name string
email string
}
func (u user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
func main() {
u1 := user{
name: "john smith",
email: "john@yahoo.com",
}
u1.notify()
}
在这里notify
的接受者类型和notify
的调用者类型可以组出四种组合而不报错:
func (u user) notify()
u1.notify()
func (u *user) notify()
u1.notify()
func (u user) notify()
(&u1).notify()
func (u *user) notify()
(&u1).notify()
方法的调用者如果是地址,可以被转化为值;如果是值可以被转化为指针。
编译器你给我听好了!那如果这样的糖都能实现,那我教教你应该怎么做才足够“优雅”,还是方法集的错误示例代码:
package main
import "fmt"
type notifyInterface interface {
notify()
}
func sendEmail(n notifyInterface) {
n.notify()
}
type user struct {
name string
email string
}
func (u *user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
func main() {
u1 := user{
name: "john smith",
email: "john@yahoo.com",
}
sendEmail(u1)
}
咱们就先把值类型的user
放行到sendEmail
方法里进来,你先别惦记你那方法集规则了!然后再通过你强大的隐式转化之后去调用notify
方法,这样sendEmail
方法里的参数是什么类型都不重要了,反正作为调用者可以支持与接受者各种类型的组合,完成了类型的解耦,是不是非常的“优雅”?
编译器:说的好!但在我这就是要先检查方法集规则,是不可能放你一个值类型进入sendEmail
方法的!
我:啊这。。。。。。。。
说人话
以上就是我的一些思考,这个方法集规则和方法调用者的语法糖真的让我感到一股违和感。。。。。当然我知道自己是初学go,水平不足,设计go的大牛肯定想的更多,但我作为初学者确实感觉到一点奇怪,到底为什么要设置这个方法集规则呢?为什么不提供随时随地的值和地址之间的隐式的转化呢?
这篇文章就是我的自言自语,可能哪些地方有明显的逻辑漏洞啥的请帮我指出来~知道原因的大佬们也麻烦指导指导吧~