Go语言接口方法集的问题

·  阅读 162

先来看一段代码

package main

import (
    "fmt"
)
type notifier interface {
    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() {
    u := user{"Bill", "bill@email.com"}
    sendNotification(u)
}
func sendNotification(n notifier) {
    n.notify()
}
复制代码

这个例子来自《Go语言实战》的第五章lisitening36。运行这个例子,不出所料的话,会报错,详情如下:

./listing36.go:33:18: cannot use u (type user) as type notifier in argument to sendNotification:
        user does not implement notifier (notify method has pointer receiver)
复制代码

先说明一下,sendNotification方法能够接受一个实现类(比如user)来调用notifier接口中notify(),这是毋庸置疑的。

错误提示的意思就是:编译器告诉我们 user类型的值并没有实现这个接口。问题是我们明明已经实现了,编译器却不让我们通过,这是为什么。其实错误信息也已经明确告诉我们了:notify method has pointer receiver。notify方法接收者是指针,但是我们调用的时候sendNotification(u),是通过值调用,所以报错了。

规范里描述的方法集

Values Methods      Receivers
--------------------------------
T                   (t T)
*T                  (t T) and (t *T)
复制代码

描述中说到,T 类型的值的方法集只包含值接收者声明的方法,而指向 T 类型的指针的方法集既包含值接收者声明的方法,也包含指针接收 者声明的方法

另一个角度看方法集

Receivers            Values Methods      
--------------------------------
(t T)                T and *T
(t *T)               *T
复制代码

从这里可以看到,如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。

看回原来代码

u := user{"Bill", "bill@email.com"}
sendNotification(u)
复制代码

这下知道是什么问题了吧?我们把u(实现类)的值作为参数传入sendNotification方法中,这是不允许的。因为实现notify() 方法时,是以指针的形式作为接收者(func (u *user) notify() ),所以这里应该改为sendNotification(&u)。或者将func (u user) notify() 的user 改为 user。问题即可以解决。

分类:
后端
标签: