在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的方法
}
複製代碼