go语言方法集的一些延伸思考

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的大牛肯定想的更多,但我作为初学者确实感觉到一点奇怪,到底为什么要设置这个方法集规则呢?为什么不提供随时随地的值和地址之间的隐式的转化呢?
这篇文章就是我的自言自语,可能哪些地方有明显的逻辑漏洞啥的请帮我指出来~知道原因的大佬们也麻烦指导指导吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值