主要区分一下两个方面的内容:

  • 单纯的方法定义
  • 通过接口传递参数

1、 单纯的方法定义

go语言内部会自动进行值和指针的转换, 代码在编译的时候不会出错;区别在于使用指针定义方法,方法操作的是该数据本身;而使用值定义方法时,方法操作的是该数据的拷贝。

总结:如果使用除接口类型以外的类型作为接收者时,使用值和指针调用方法不会出现编译错误; 如果使用接口类型的变量(实现了该接口)调用方法时,使用值调用指针定义的方法时会出现编译出错。

1、 使用值定义方法,使用值调用方法的情况

type user struct {
    name string
    email string
}

func (u user) notify() {
    //将传入的参数复制一份,赋值给u
    u.name = "Jack"
    fmt.Println("Send email to", u.name, u.email)
}

func main() {
    user := user{"Andy", "1139329@163.com"}
    user.notify()   
    fmt.Println(user)
}

输出(名子并不会改变):
Send email to Jack 1139329@163.com
{Andy 1139329@163.com}

2、 使用值定义方法,使用指针调用方法的情况

由于定义方法时使用的是值,在编译过程中会对调用者为指针的类型进行解引用,内部实现为 *user.notify()

type user struct {
    name string
    email string
}

func (u user) notify() {
    //将传入的参数复制一份,赋值给u
    u.name = "Jack"
    fmt.Println("Send email to", u.name, u.email)
}

func main() {
    user := &user{"Andy", "1139329@163.com"}
    user.notify()   
    fmt.Println(user)
}

输出(名子也不会改变):
Send email to Jack 1139329@163.com
{Andy 1139329@163.com}

3、 使用指针定义方法,使用指针调用方法的情况

type user struct {
    name string
    email string
}

func (u *user) notify() {
    u.name = "Jack"
    fmt.Println("Send email to", u.name, u.email)
}

func main() {
    user := user{"Andy", "1139329@163.com"}
    user.notify()
    fmt.Println(user)
}

输出(名子会改变):
Send email to Jack 1139329@163.com
{Jack 1139329@163.com}

4、 使用指针定义方法,使用值调用方法的情况

内部实现为 *user.notify()

type user struct {
    name string
    email string
}

func (u *user) notify() {
    u.name = "Jack"
    fmt.Println("Send email to", u.name, u.email)
}

func main() {
    user := user{"Andy", "1139329@163.com"}
    user.notify()
    fmt.Println(user)
}

输出(名子会改变):
Send email to Jack 1139329@163.com
{Jack 1139329@163.com}

2、 通过接口传递参数

1、 接受者receiver为值,使用值传递的情况

type user struct {
    name  string
    email string
}

type notifyInterface interface {
    notify()
}

func (u user) notify() {
    fmt.Println("Send email to", u.name, u.email)
}

func sendNotification(n notifyInterface) {
    n.notify()
}

func main() {
    user := user{"Andy", "1139329@163.com"}
    sendNotification(user)
}

//编译成功

2、 接受者receiver为值,使用指针传递的情况

type user struct {
    name  string
    email string
}

type notifyInterface interface {
    notify()
}

func (u user) notify() {
    fmt.Println("Send email to", u.name, u.email)
}

func sendNotification(n notifyInterface) {
    n.notify()
}

func main() {
    user := &user{"Andy", "1139329@163.com"}
    sendNotification(user)
}

//编译成功

3、 接受者receiver为指针,使用指针传递的情况

type user struct {
    name  string
    email string
}

type notifyInterface interface {
    notify()
}

func (u *user) notify() {
    fmt.Println("Send email to", u.name, u.email)
}

func sendNotification(n notifyInterface) {
    n.notify()
}

func main() {
    user := &user{"Andy", "1139329@163.com"}
    sendNotification(user)
}

//编译成功

4、 接受者receiver为指针,使用值传递的情况

type user struct {
    name  string
    email string
}

type notifyInterface interface {
    notify()
}

func (u *user) notify() {
    fmt.Println("Send email to", u.name, u.email)
}

func sendNotification(n notifyInterface) {
    n.notify()
}

func main() {
    user := user{"Andy", "1139329@163.com"}
    sendNotification(user)
}

//编译失败(使用指针接受者来实现一个接口,值类型无法实现对应的接口)
    cannot use user (type user) as type notifyInterface in argument to sendNotification:
    user does not implement notifyInterface (notify method has pointer receiver)

针对以上情况,《Go语言实战》一书中这样讲到,首先这是Go语言的一种规则,具体如下:如果使用指针接受者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接受者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。

为什么会有这样的限制呢:作者解释为go编译器并不总能自动获得一个值得地址!