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