2023年6月

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

1、new (T) 为每个新的类型 T 分配一片内存,初始化为零值,并且返回类型为 * T 的内存地址,这种方法返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}。

2、make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:slice、map 和 channel类型。

new () 是一个函数,不要忘记它的括号

1、使用微信提供的证书工具生成微信支付商户证书

apiclient_cert.pem, apiclient_key.pem

2、使用 cmd 生成微信支付商户证书序列号

openssl x509 -in apiclient_key.pem -noout -serial

3、使用 cmd 生成微信支付平台证书 (需要 composer 安装 wechatpay/wechatpay)

# -k: 微信支付商户 APIv3 密钥, -m: 商户号, -f: 商户的私钥文件路径, -s: 商户证书序列号, -o: 平台证书保存路径
php vendor\bin\CertificateDownloader.php -k 03ea86*** -m 16442*** -f ***\storage\cert\apiclient_key.pem -s 1208B*** -o ***\Desktop\WXCertUtil\cert

4、完整配置信息

# 微信支付 APP
# APP ID
WECHAT_APP_ID=***
# 微信支付商户 ID
WECHAT_MCH_ID=***
# 微信支付商户 APIv2 密钥
WECHAT_MCH_SECRET=***
# 微信支付商户 APIv3 密钥
WECHAT_MCH_V3_SECRET=***
# 微信支付商户证书序列号
WECHAT_SERIAL=***
# 微信支付商户证书路径
WECHAT_CERT=***\storage\cert\apiclient_cert.pem
WECHAT_KEY=***\storage\cert\apiclient_key.pem
# 微信支付平台证书序列号
WECHAT_PLATFORM_SERIAL=***
# 微信支付平台证书路径
WECHAT_PLATFORM_PEM=***\storage\cert\wechatpay.pem

转自:https://learnku.com/articles/78016
感谢作者的辛苦付出。