go 指針接收者和值接收者的區(qū)別小結(jié)
go 指針接收者和值接收者的區(qū)別
指針接收者和值接收者的區(qū)別主要有兩點:
- Go 中函數(shù)傳參是傳值,因此指針接收者傳遞的是接收者的指針拷貝,值接收者傳遞的是接收者的拷貝---在方法中指針接收者的變量會被修改,而值接收者的成員變量修改是無效的(畢竟傳入的是拷貝,修改的自然也是拷貝的成員變量)。
- 在接口實現(xiàn)上,如果值接收者實現(xiàn)了某個方法,相當(dāng)于值接收者和指針接收者都實現(xiàn)了這個方法。反之則不行,即指針接收者實現(xiàn)了某個方法,不能看成值接收者實現(xiàn)這個方法。
注意第2點只影響接口實現(xiàn),如果不涉及接口的話是不受到這條規(guī)則約束的。
一個助記的但是不太正確的觀點:接收者是指針類型的方法,很可能在方法中會對接收者的屬性進(jìn)行更改操作,從而影響接收者;而對于接收者是值類型的方法,在方法中不會對接收者本身產(chǎn)生影響。
理解角度:指針接收者可以選擇修改自己的值和不修改,值接收者修改自己的值是無效的(因為是拷貝)。所以可以看成指針接收者的能力>值接收者的能力,自然:指針接收者實現(xiàn)某個方法可以看成值接收者實現(xiàn)某個方法,反之則不行。
package main import "fmt" type coder interface { code() debug() } type Gopher struct { language string } func (p Gopher) code() { fmt.Printf("I am coding %s language\n", p.language) } func (p *Gopher) debug() { fmt.Printf("I am debuging %s language\n", p.language) } func main() { var c coder = &Gopher{"Go"} //運行正常 //上一行換成: var c coder = Gopher{"Go"} // 則報錯 Gopher does not implement coder (debug method has pointer receiver) c.code() c.debug() }
注意??:對于????上面的第 2 點補(bǔ)充:雖然隱式實現(xiàn)的方法不一樣,但是如果不涉及接口,單純在調(diào)用的時候,無論是指針接收者還是值接收者實現(xiàn)了某個方法,指針接收者和值接收者都可以調(diào)用。本質(zhì)上是 go 的語法糖。
最佳實踐:
如果類型具備“原始的本質(zhì)”,即其成員都是由 Go 語言里內(nèi)置的原始類型,如字符串,整型值等,那就定義值接收者類型的方法。
內(nèi)置的引用類型,如 slice,map,interface,channel,這些類型比較特殊,聲明他們的時候,實際上是創(chuàng)建了一個 header?, 對于他們也是直接定義值接收者類型的方法。這樣,調(diào)用函數(shù)時,是直接 copy 了這些類型的 header?,而 header? 本身就是為復(fù)制設(shè)計的。
如果類型具備非原始的本質(zhì),不能被安全地復(fù)制,這種類型總是應(yīng)該被共享,那就定義指針接收者的方法。
雖然上面是這么說,但是個人感覺在實際使用中,基本可以無腦使用指針接收者。主要原因在于:1.指針接收者沒有值拷貝帶來的巨大開銷。2.如果就想在函數(shù)中修改值就必須使用指針接收者。3.值接收者唯一的優(yōu)勢就是為了防止意外的修改,為了防止這一點可以通過創(chuàng)建一個函數(shù)或者方法來手動 copy,而這并不會帶來多大的開銷。
易錯點辨析
是否改變結(jié)構(gòu)體的值看的是方法是指針接收者還是值接收者,而不是看調(diào)用方是指針還是值。原因在于golang編譯器在背后會完成一些工作,比如:解引用,隱式使用引用。代碼范例如下:
package main import "fmt" type Node struct { val int } func (receiver Node) changeVal1() { receiver.val++ } func (receiver *Node) changeVal2() { receiver.val++ } func main() { someOne := Node{} someOne.changeVal1() fmt.Printf("%d\n", someOne.val) //0 ,說明沒有改值,因為方法接收者是值接收者 someOnePtr := &Node{} someOnePtr.changeVal1() fmt.Printf("%d\n", someOnePtr.val) //0 ,說明沒有改值,因為方法接收者是值接收者,與調(diào)用方是否指針無關(guān) someTwo := Node{} someTwo.changeVal2() fmt.Printf("%d\n", someTwo.val) //1 ,說明成功改值,因為方法接收者是指針接收者,與調(diào)用方是否指針無關(guān) someTwoPtr := &Node{} someTwoPtr.changeVal2() fmt.Printf("%d\n", someTwoPtr.val) //1 ,說明成功改值,因為方法接收者是指針接收者,與調(diào)用方是否指針無關(guān) }
參考:https://golang.design/go-questions/interface/receiver/
到此這篇關(guān)于go 指針接收者和值接收者的區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)go 指針接收者和值接收者內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解prometheus監(jiān)控golang服務(wù)實踐記錄
這篇文章主要介紹了詳解prometheus監(jiān)控golang服務(wù)實踐記錄,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11goland安裝1.7版本報錯Unpacked?SDK?is?corrupted解決
這篇文章主要為大家介紹了goland安裝1.7版本報錯Unpacked?SDK?is?corrupted解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Go結(jié)合Redis用最簡單的方式實現(xiàn)分布式鎖
本文主要介紹了Go結(jié)合Redis用最簡單的方式實現(xiàn)分布式鎖示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01Golang基礎(chǔ)之函數(shù)使用(參數(shù)傳值)實例詳解
這篇文章主要為大家介紹了Golang基礎(chǔ)之函數(shù)使用(參數(shù)傳值)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10