Go方法接收者值接收者與指針接收者詳解
引言
在review 一些代碼中,發(fā)現(xiàn)經(jīng)常某個類型定義的方法,其接收者既有值類型,又有指針類型,然后 Goland 就有提示: Struct Person has methods on both value and pointer receivers. Such usage is not recommended by the Go Documentation.
一般來講,這個提示對代碼的運(yùn)行并不會產(chǎn)生什么問題。只不過對于有輕微 “代碼潔癖” 的人來講,體感不好,就一定想要改統(tǒng)一。
當(dāng)然,我并不是想講要統(tǒng)一的問題,前面說這么多廢話,只是為了鋪墊一下引出本文的內(nèi)容:Go中的值接收者與指針接收者有什么關(guān)系與區(qū)別,該怎么選?
聯(lián)系與區(qū)別
在繼續(xù)講下去之前,我們得先明確,Go 里邊能夠定義方法的必須是自定義類型,而不能是系統(tǒng)內(nèi)置類型,比如 int、string 這種是不可以為其添加方法的。
那么當(dāng)我們定義了一個自定義類型,可以為其添加方法,先上代碼:
package main import "fmt" type Person struct { name string age int } // 值針接收者 func (p Person) GetName() string { return p.name } // 指針接收者 func (p *Person) GetAge() int { return p.age } func main() { // 定義了一個【值類型】 t := Person{ name: "DaYu", age: int(28), } // 調(diào)用值方法 fmt.Println(t.GetName()) // 調(diào)用指針方法 fmt.Println(t.GetAge()) } -----運(yùn)行結(jié)果------- study/demo01/client go run * DaYu 28
指針類型調(diào)用結(jié)果
從使用過程看,值類型的變量,可以調(diào)用該類型的值接收者方法,也可以調(diào)用指針接收者方法。
反之,我們可以定義一個指針類型,然后看看調(diào)用結(jié)果:
package main import "fmt" type Person struct { name string age int } func (p *Person) GetName() string { return p.name } func (p Person) GetAge() int { return p.age } func main() { // 注意,其它地方都沒有改,只是這里改變了類型 t := &Person{ name: "DaYu", age: int(28), } fmt.Println(t.GetName()) fmt.Println(t.GetAge()) } -----運(yùn)行結(jié)果------- study/demo01/client go run * DaYu 28
這段代碼告訴我們,指針類型的變量,可以調(diào)用該類型的值接收者方法,也可以調(diào)用指針接收者方法。
是不是特別有意思?
- 值類型變量,可以調(diào)用值接收的方法,也可以調(diào)用指針接收者的方法;
- 指針類型變量,可以調(diào)用值接收的方法,也可以調(diào)用指針接收者的方法。
看起來好像兩者對等的,并沒有差別。那么二者真的沒有差別嗎?只是一種表達(dá)形式上的差異?其實(shí)不然,如果引入接口類型后,我們再來看看。
package main // 新增的接口 type Animal interface { GetName() string GetAge() int } type Person struct { name string age int } func (p *Person) GetName() string { return p.name } func (p Person) GetAge() int { return p.age } func main() { // 定義的接口變量 var ani Animal // person 實(shí)現(xiàn)了 Animal 接口,賦值給了 ani 變量 // 但是,這里編譯會通不過,錯誤如下: // Cannot use 'Person{ name: "DaYu", age: int(28), }' (type Person) as the type Animal Type does not implement 'Animal' as the 'GetName' method has a pointer receiver ani = Person{ name: "DaYu", age: int(28), } ani.GetName() ani.GetAge() }
為什么會報(bào)錯呢? 錯誤提醒很明顯了:Person 沒有實(shí)現(xiàn) Animal 的 GetName 方法。因?yàn)樵谏厦娴拇a中,我們實(shí)現(xiàn) GetName 方法的是 (*Person) 類型。
但是為什么 GetAge 方法不報(bào)錯呢? 那是因?yàn)?Go 里邊對于 (Type)Method 的方法,會自動讓他擁有 (*Type)Method 方法的能力。
實(shí)現(xiàn)接口時約束
- 如果定義的是 (Type)Method,則該類型會隱式的聲明一個 (*Type)Method;
- 如果定義的是 (*Type)Method ,則不會隱式什么一個 (Type)Method。
至于為什么不也隱式申明一個 (Type)Method ,我覺得有一個原因是,我們一般采用指針接收者時,方法內(nèi)部改變的值,接收者本身也會改變,那么此時如果隱式有這樣一個申明,外部使用值類型時,這個改變就不會生效,語義上就會非常奇怪。
該怎么用
從使用表現(xiàn)上看,指針接收者在方法內(nèi)部的改變,會體現(xiàn)到其本身。但這并不是決定我們要不要用指針接收者的唯一理由! 最重要的還是看接收者要不要全局共享一個實(shí)體,其次某些場景下,如果接收者本身太大,拷貝成本很高,也應(yīng)該使用指針接收者。
回到文檔開篇的問題,為什么不建議值接收者、指針接收者混用,主要還是在于語義不夠清晰,存在潛在理解成本的問題。
以上就是Go方法接收者值接收者與指針接收者詳解的詳細(xì)內(nèi)容,更多關(guān)于Go方法值接收者指針接收者的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang網(wǎng)絡(luò)socket粘包問題的解決方法
這篇文章主要介紹了golang網(wǎng)絡(luò)socket粘包問題的解決方法,簡單講述了socket粘包的定義并結(jié)合實(shí)例形式分析了Go語言解決粘包問題的方法,需要的朋友可以參考下2016-07-07詳解如何在Golang中實(shí)現(xiàn)CORS(跨域)
很多時候,需要允許Web應(yīng)用程序在不同域之間(跨域)實(shí)現(xiàn)共享資源,本文將簡介跨域、CORS的概念,以及如何在Golang中如何實(shí)現(xiàn)CORS,文中有詳細(xì)的示例代碼,需要的朋友可以參考下2023-10-10golang接口實(shí)現(xiàn)調(diào)用修改(值接收者指針接收者)場景詳解
這篇文章主要為大家介紹了golang接口實(shí)現(xiàn)調(diào)用修改值接收者指針接收者示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08基于Go語言構(gòu)建RESTful API服務(wù)
在實(shí)際開發(fā)項(xiàng)目中,你編寫的服務(wù)可以被其他服務(wù)使用,這樣就組成了微服務(wù)的架構(gòu);也可以被前端調(diào)用,這樣就可以前后端分離。那么,本文主要介紹什么是 RESTful API,以及 Go 語言是如何玩轉(zhuǎn) RESTful API 的2021-07-07使用golang獲取linux上文件的訪問/創(chuàng)建/修改時間
這篇文章主要介紹了使用golang獲取linux上文件的訪問/創(chuàng)建/修改時間,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08Golang操作sqlite3數(shù)據(jù)庫的詳細(xì)教程
最近會使用到sqlite3,這里作個記錄,記性越來越差就是這樣,下面這篇文章主要給大家介紹了關(guān)于Golang操作sqlite3數(shù)據(jù)庫的詳細(xì)教程,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04