在Go的类方法中,分为值接收者方法和指针接收者方法,对于刚开始接触Go的同窗来讲,有时对Go的方法会感到困惑。下面咱们结合题目来学习Go的方法。golang
为了方便叙述,下文描述的值接收者方法简写为值方法,指针接收者方法简写为指针方法。c#
下面代码中,哪段编号的代码会报错?具体报什么错误?数组
type Animal interface {
Bark()
}
type Dog struct {
}
func (d Dog) Bark() {
fmt.Println("dog")
}
type Cat struct {
}
func (c *Cat) Bark() {
fmt.Println("cat")
}
func Bark(a Animal) {
a.Bark()
}
func getDog() Dog {
return Dog{}
}
func getCat() Cat {
return Cat{}
}
func main() {
dp := &Dog{}
d := Dog{}
dp.Bark() // (1)
d.Bark() // (2)
Bark(dp) // (3)
Bark(d) // (4)
cp := &Cat{}
c := Cat{}
cp.Bark() // (5)
c.Bark() // (6)
Bark(cp) // (7)
Bark(c) // (8)
getDog().Bark() // (9)
getCat().Bark() // (10)
}
复制代码
抛砖引玉,让咱们学习完再来做答。学习
咱们来看看值方法的声明。spa
type Dog struct {
}
func (d Dog) Bark() {
fmt.Println("dog")
}
复制代码
上面代码中,方法Bark
的接收者是值类型,那么这就是一个值接收者的方法。指针
下面再看看指针接收者的方法。code
type Cat struct {
}
func (c *Cat) Bark() {
fmt.Println("cat")
}
复制代码
这个在Go文档里有定义:cdn
T
,它的方法集合是全部接收者为T
的方法。*T
,它的方法集合是全部接收者为*T
和T
的方法。Values | Method Sets |
---|---|
T | (t T) |
*T | (t T) and (t *T) |
指针*T
接收者方法:只有指针类型*T
才能调用,但其实值T
类型也能调用,为何呢?由于当使用值调用t.Call()
时,Go会转换成(&t).Call()
,也就是说最后调用的仍是接收者为指针*T
的方法。接口
但要注意t是要能取地址才能这么调用,好比下面这种状况就不行:文档
func getUser() User {
return User{}
}
...
getUser().SayWat()
// 编译错误:
// cannot call pointer method on aUser()
// cannot take the address of aUser()
复制代码
值T
接收者方法: 指针类型*T
和值T
类型都能调用。
Methods Receivers | Values |
---|---|
(t T) | T and *T |
(t *T) | *T |
使用接收者为*T
的方法实现一个接口,那么只有那个类型的指针*T
实现了对应的接口。
若是使用接收者为T
的方法实现一个接口,那么这个类型的值T
和指针*T
都实现了对应的接口。
在给类声明方法时,方法接收者的类型要统一,最好不要同时声明接收者为值和指针的方法,这样容易混淆而不清楚到底实现了哪些接口。
下面咱们来看看哪一种类型适合声明接收者为值或指针的方法。
下面这2种状况请务必声明指针接收者方法:
sync.Mutex
或相似锁的变量,由于它们不容许值拷贝。下面这2种状况也建议声明指针接收者方法:
下面这些状况建议使用值接收者方法:
map
,func
,channel
。int
,string
。type Animal interface {
Bark()
}
type Dog struct {
}
func (d Dog) Bark() {
fmt.Println("dog")
}
type Cat struct {
}
func (c *Cat) Bark() {
fmt.Println("cat")
}
func Bark(a Animal) {
a.Bark()
}
func getDog() Dog {
return Dog{}
}
func getCat() Cat {
return Cat{}
}
func main() {
dp := &Dog{}
d := Dog{}
dp.Bark() // (1) 经过
d.Bark() // (2) 经过
Bark(dp)
// (3) 经过,上面说了类型*Dog的方法集合包含接收者为*Dog和Dog的方法
Bark(d) // (4) 经过
cp := &Cat{}
c := Cat{}
cp.Bark() // (5) 经过
c.Bark() // (6) 经过
Bark(cp) // (7) 经过
Bark(c)
// (8) 编译错误,值类型Cat的方法集合只包含接收者为Cat的方法
// 因此T并无实现Animal接口
getDog().Bark() // (9) 经过
getCat().Bark()
// (10) 编译错误,
// 上面说了,getCat()是不可地址的
// 因此不能调用接收者为*Cat的方法
}
复制代码