Go面試必考題目之method篇
  在 Go 的類方法中,分為值接收者方法和指標接收者方法,對於剛開始接觸 Go 的同學來說,有時對 Go 的方法會感到困惑。下面我們結合題目來學習 Go 的方法。
  為了方便敘述,下文描述的值接收者方法簡寫為值方法,指標接收者方法簡寫為指標方法。
  下面程式碼中,哪段編號的程式碼會報錯?具體報什麼錯誤?
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)
}
  拋磚引玉,讓我們學習完再來作答。
值方法和指標方法
我們來看看值方法的宣告。
type Dog struct {
}
func (d Dog) Bark() {
fmt.Println("dog")
}
上面程式碼中,方法Bark
的接收者是值型別,那麼這就是一個值接收者的方法。
下面再看看指標接收者的方法。
type Cat struct {
}
func (c *Cat) Bark() {
fmt.Println("cat")
}
類的方法集合
這個在 Go 文件裡有定義:
- 對於型別
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的方法
}
總結
- 理清型別的方法集合。
- 理清接收者方法的呼叫範圍。
參考文獻
- 《Pointer vs Value receiver》 https://yourbasic.org/golang/pointer-vs-value-receiver/
- 《Method sets》 https://golang.org/ref/spec#Method_sets
- https://stackoverflow.com/questions/19433050/go-methods-sets-calling-method-for-pointer-type-t-with-receiver-t?rq=1
感謝閱讀,歡迎大家指正,留言交流~
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Go面試必考題目之slice篇Go面試
- 分享一道Go面試必考的題Go面試
- 面試必考:網路問題面試
- 100家IT 名企面試必考面試題java系列面試題Java
- 前端面試必考題:React Hooks 原理剖析前端面試ReactHook
- Python面試中8個必考問題Python面試
- Python 面試中 8 個必考問題Python面試
- Java面試題:之ZooKeeper篇Java面試題
- 面試題整理之Vue篇面試題Vue
- 大型網際網路公司必考java面試題與面試技巧Java面試題
- js面試必考三問JS面試
- 面試題聯盟之 VUE 篇面試題Vue
- Android面試題之Fragment篇Android面試題Fragment
- 面試題目面試題
- 2018最新《BAT Java必考面試題集》BATJava面試題
- 前端面試題目蒐集——理論知識篇前端面試題
- Go 面試題Go面試題
- 限時看!阿里、華為資料結構面試必考題!阿里資料結構面試
- 「高頻必考」Docker&K8S面試題和答案DockerK8S面試題
- Go語言之methodGo
- TX 面試題目面試題
- JavaScript面試題目,JavaScript面試題
- 面試題目收集面試題
- DBA 面試題目面試題
- 面試題目(zt)面試題
- ABAP面試題目面試題
- SQL面試必考——計算留存率SQL面試
- Java最全面試題之Spring篇Java面試題Spring
- 持續輸出面試題之RocketMQ篇面試題MQ
- Android面試題之Gradle配置篇Android面試題Gradle
- iPhone 常用面試題目iPhone面試題
- 前端面試題目前端面試題
- python面試題目Python面試題
- Java面試必考題之執行緒的生命週期,結合原始碼,透徹講解!Java面試執行緒原始碼
- 前端面試送命題:面試題篇前端面試題
- 持續輸出面試題系列之ZooKeeper篇面試題
- 持續輸出面試題之Hibernate篇面試題
- JAVA框架面試題整理之—第四篇Java框架面試題