package main

import (
    "log"
)

type Notifier interface {
    Notify() error
}

type User struct {
    Name  string
    Email string
}

func (u *User) Notify() error {
    log.Printf("User: Sending user Email To %s<%s>", u.Name, u.Email)
    return nil
}

func SendNotification(notify Notifier) error {
    return notify.Notify()
}

func main() {
    user := User{
        Name:  "liziyu",
        Email: "13073932@163.com",
    }
    //user.Notify()
    SendNotification(user)
    //cannot use user (variable of type User) as Notifier value in argument to SendNotification: User does not implement Notifier (method Notify has pointer receiver)
}

cannot use user (variable of type User) as Notifier value in argument
to SendNotification: User does not implement Notifier (method Notify
has pointer receiver)

原因:

上面的错误说明Notify方法的接受者是指针类型的,因此User类型的值并没有实现该方法,也就是没有实现Notifier接口,不能作为参数传入。

为什么编译器不认为值类型实现了接口呢?确定接口是否被实现的规则是基于这些方法的接收者以及如何进行接口调用。下面是关于编译器如何确定类型的值或指针是否实现接口的规则:

指针类型(T)的方法集包含接受者是值类型(T)和指针类型(T)的方法。

该规则表明,如果用于调用接口方法的变量是指针,那么不管方法的接受者是值类型还是指针类型都可以被调用。此规则不适用于我们的示例,因为我们向SendNotification函数传递一个值不是指针。

值类型(T)的方法集只包含所有接受者为值类型(T)的方法

该规则表明,如果我们用于调用接口的变量是值类型,那么只有接收者也是值类型的方法才能满足接口。此规则不适用于我们的示例,因为Notify方法的接收者是一个指针。

换句话说:

接收者为值类型(T)的方法集不包含接收者为指针类型(*T)的方法。

这就是我们的前面代码报错的情况。Notify方法的接收者是指针,我们使用值进行接口方法调用会报错。要解决这个问题,我们只需要将User值的地址传递给SendNotification函数即可:
func main() {
    user := &User{
        Name:  "liziyu",
        Email: "13073932@163.com",
    }

    SendNotification(user)
}

// Output:
User: Sending User Email To liziyu<13073932@163.com>



最后用两句不太严谨的话总结一下:

1、当类型(结构体或标准类型)对象为“值类型”时,该对象只能调用它的接受者为“值类型”的接口方法。
2、当类型(结构体或标准类型)对象为“指针类型”时,该对象不但能调用它的接受者为“值类型”的接口方法,还能调用接受者为“指针类型”的接口方法。

->>>>>>>>>>>>>>>>>>>>>>以下代码有同工异曲之妙<<<<<<<<<<<<<<<<<<-

type F interface {
  f()
}

type S1 struct{}

func (s S1) f() {}

type S2 struct{}

func (s *S2) f() {}

s1Val := S1{}
s1Ptr := &S1{}
s2Val := S2{}
s2Ptr := &S2{}

var i F
i = s1Val
i = s1Ptr
i = s2Ptr

//  下面代码无法通过编译。因为 s2Val 是一个值,而 S2 的 f 方法中没有使用值接收器
//   i = s2Val
//   i.f() 报错 :cannot use s2Val (variable of type S2) as F value in assignment: S2 does not implement F (method f has pointer receiver)

QQ20230625-113225@2x.png

链接:https://www.jianshu.com/p/59a73d426e59