golang指針接收者和值接收者方法調用筆記


初學go時很多同學會把 值接收者 和 指針接收者 的方法相互調用搞混淆,好多同學都只記得指針類型可以調用值接收者方法指針接收者方法,而值類型只能調用值接收者方法,其實不然,在某些情況下,值類型也是可以調用指針接收者方法的。

最近又看到有同學發出了這樣的疑問,所以打算記錄一下,用以備忘、分享。

類型不同可以調用

package main import ( "fmt" ) type field struct { name string } func (p *field) pointerMethod() { fmt.Println(p.name) } func (p field) valueMethod() { fmt.Println(p.name) } func main() { fp := &field{name: "pointer"} fv := field{name: "value"} fp.pointerMethod() fp.valueMethod() fv.pointerMethod() fv.valueMethod() } //output: pointer pointer value value 

首先我們給field定義了兩個方法,一個是指針接收者的pointerMethod,一個是值接收者valueMethod

然后我們創建了變量,fp是指針類型,fv是值類型。

fpfv分別調用pointerMethodvalueMethod,可以看到他們都可以通過編譯正常輸出。

當類型和方法的接收者類型不同時,編譯器會做一些操作:

值類型調用指針接收者方法時,實際為(&fv).pointerMethod()

指針類型調用值接收者方法時,實際為(*fp).valueMethod()

這里是類型不同可以相互調用的情況,再說下不能調用的情況。

類型不同不可以調用

不能調用的情況有兩種:

  • 值類型不能被尋址
  • 指針接收者實現接口

兩種情況都是值類型不能調用指針接收者方法

值類型不能被尋址

如果值類型實體不能被尋址,那么它就不能調用指針接收者方法

package main import ( "fmt" ) type field struct { name string } func (p *field) pointerMethod() { fmt.Println(p.name) } func (p field) valueMethod() { fmt.Println(p.name) } func NewFiled() field { return field{name: "right value struct"} } func main() { NewFiled().valueMethod() NewFiled().pointerMethod() } 

運行代碼報錯:

./x.go:37:12: cannot call pointer method on NewFiled() ./x.go:37:12: cannot take the address of NewFiled() 

看來編譯器首先試着給 NewFoo() 返回的右值調用 pointer method,出錯;然后試圖給其插入取地址符,未果,就只能報錯了。

至於左值和右值的區別,大家感興趣可以自行搜索一下。大致來說,最重要區別就是是否可以被尋址,可以被尋址的是左值,既可以出現在賦值號左邊也可以出現在右邊;不可以被尋址的即為右值,比如函數返回值、字面值、常量值等等,只能出現在賦值號右邊。

指針接收者實現接口

使用指針接收者實現接口方法,那么只有指針類型的實體實現了接口

package main import "fmt" type human interface { speak() sing() } type man struct { } func (m man) speak() { fmt.Println("speaking") } func (m *man) sing() { fmt.Println("singing") } func main() { var h human = &man{} h.speak() h.sing() } 

 

上面代碼可以正常編譯輸出,但是我們把&man{}修改為man{}就編譯不過了

func main() { var h human = man{} h.speak() h.sing() } 

 

報錯如下:

./x.go:22:6: cannot use man literal (type man) as type human in assignment: man does not implement human (sing method has pointer receiver)

man沒有實現human,因為sing是個指針接收者方法。

這里看下飛雪無情的總結:

實體類型以指針接收者實現接口的時候,只有指向這個類型的指針才被認為實現了該接口

如果是值接收者,實體類型的值和指針都可以實現對應的接口;如果是指針接收者,那么只有類型的指針能夠實現對應的接口

再看下饒大的解釋:

接收者是指針類型的方法,很可能在方法中會對接收者的屬性進行更改操作,從而影響接收者;而對於接收者是值類型的方法,在方法中不會對接收者本身產生影響。

所以,當實現了一個接收者是值類型的方法,就可以自動生成一個接收者是對應指針類型的方法,因為兩者都不會影響接收者。但是,當實現了一個接收者是指針類型的方法,如果此時自動生成一個接收者是值類型的方法,原本期望對接收者的改變(通過指針實現),現在無法實現,因為值類型會產生一個拷貝,不會真正影響調用者。

平時我們寫代碼的時候也不用可以記這個,不僅編譯器會報錯,goland也一樣會提示。

參考

https://www.qtmuniao.com/2020/01/06/go-value-pointer-method/

https://qcrao91.gitbook.io/go/interface/zhi-jie-shou-zhe-he-zhi-zhen-jie-shou-zhe-de-qu-bie

https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM