golang中接口赋值与方法集
source link: https://studygolang.com/articles/15278?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
接口使用疑问
golang
中的接口可以轻松实现 C++
中的多态,而且没有 继承自同一父类
的限制,感觉方便很多。但是在使用的时候,如果没有理解,也可能会遇到"坑"。比如 《Go语言实战》
中的一个例子:
package main import "fmt" type user struct { name string email string } type notifier interface { notify() } func (u *user) notify() { fmt.Printf("sending user email to %s<%s>\n", u.name, u.email) } func sendNotification(n notifier) { n.notify() } func main() { u := user{ name: "stormzhu", email: "[email protected]", } sendNotification(u) } // compile error // cannot use u (type user) as type notifier in argument to sendNotification: // user does not implement notifier (notify method has pointer receiver)
报的错是 u
没有实现 notifier
这个接口,实现了这个接口的是 *user
类型,而不是 user
类型, u
是 user
类型,所以不能赋值给 notifier
这个接口。
既然如此,修改为 sendNotification(&u)
就OK了。然而问题是,如何理解到底是 T
类型还是 *T
类型实现了某个接口呢?
接口的定义
参考雨痕的 《Go语言学习笔记》
第七章, go
语言中的接口定义如下:
type iface struct { tab *itab // 类型信息 data unsafe.Pointer //实际对象指针 } type itab struct { inter *interfacetype // 接口类型 _type *_type // 实际对象类型 fun [1]uintptr // 实际对象方法地址 }
虽然具体的细节操作不太懂,但是可以知道,对一个接口赋值的时候,会拷贝 类型信息
和该类型的 方法集
。这就类似于 C++
多态中的 虚指针
( vptr
)和 虚函数表
( vtable
)了。我理解的是,只要这个类型的方法集中包括这个接口的所有方法,那么它就是实现了这个接口,才能够赋值给这个接口,那么问题来了,一个类型的方法集是什么呢?
方法集
同样参考雨痕 《Go语言学习笔记》 第6章6.3节,书中总结的很全面:
-
类型
T
的方法集包含所有receiver T
方法。 -
类型
*T
的方法集包含所有receiver T + *T
方法。 -
匿名嵌入
S
,类型T
的方法集包含所有receiver T + S
方法。 -
匿名嵌入
*S
,类型T
的方法集包含所有receiver T + S + *S
方法。 -
匿名嵌入
S
或*S
,类型*T
的方法集包含所有receiver T + *T + S + *S
方法。
虽然看起来比较复杂,但总结完就一话, *T
类型就是厉害,方法集包括 T
和 *T
的方法。
所以文章开头的例子中, u
是 user
类型,方法集是空的,不算是实现了 notifier
接口。
当在纠结应该将 T
类型还是 *T
类型赋值给某个接口的时候,第一步就是看方法集,看一看该类型到底有没有实现这个接口。(所以 T
和 *T
不是一个类型。。。)
一些例子
go
语言的内置库中有定义了很多接口,如 error
接口,
type error interface { Error() string }
内置的 errors
包实现了这个接口:
// Package errors implements functions to manipulate errors. package errors // New returns an error that formats as the given text. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s }
可以看到 New
方法返回值是 error
接口,而只有 *errorString
类型实现了这个接口,所以 New
方法返回的是 &errorString{text}
而不是 errorString{text}
。
总结
-
T
和*T
不是一个类型,他们的方法集不同 -
类型
*T
的方法集包含所有receiver T + *T
方法,类型T
的方法集只包含所有receiver T
方法。
参考
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK