Golang中接收者方法語法糖的使用方法詳解
1、概述
在《Golang常用語法糖》這篇博文中我們講解Golang中常用的12種語法糖,在本文我們主要講解下接收者方法語法糖。
在介紹Golang接收者方法語法糖前,先簡單說下Go 語言的指針 (Pointer),大致上理解如下:
- 變量名前的 & 符號(hào),是取變量的內(nèi)存地址,不是取值;
- 數(shù)據(jù)類型前的 * 符號(hào),代表要儲(chǔ)存的是對(duì)應(yīng)數(shù)據(jù)類型的內(nèi)存地址,不是存值;
- 變量名前的 * 符號(hào),代表從內(nèi)存地址中取值 (Dereferencing)。
注意 1:golang 指針詳細(xì)介紹請(qǐng)參見《Golang指針隱式間接引用》此篇博文。
2、接收者方法語法糖
在 Go 中,對(duì)于自定義類型 T,為它定義方法時(shí),其接收者可以是類型 T 本身,也可能是 T 類型的指針 *T。
type Instance struct{} func (ins *Instance) Foo() string { return "" }
在上例中,我們定義了 Instance 的 Foo 方法時(shí),其接收者是一個(gè)指針類型(*Instance)。
func main() { var _ = Instance{}.Foo() //編譯錯(cuò)誤:cannot call pointer method on Instance{} ,變量是不可變的(該變量沒有地址,不能對(duì)其進(jìn)行尋址操作) }
因此,如果我們用 Instance 類型本身 Instance{} 值去調(diào)用 Foo 方法,將會(huì)得到以上錯(cuò)誤。
type Instance struct{} func (ins Instance) Foo() string { return "" } func main() { var _ = Instance{}.Foo() // 編譯通過 }
此時(shí),如果我們將 Foo 方法的接收者改為 Instance 類型,就沒有問題。
這說明,定義類型 T 的函數(shù)方法時(shí),其接收者類型決定了之后什么樣的類型對(duì)象能去調(diào)用該函數(shù)方法。但,實(shí)際上真的是這樣嗎?
type Instance struct{} func (ins *Instance) String() string { return "" } func main() { var ins Instance _ = ins.String() // 編譯器會(huì)自動(dòng)獲取 ins 的地址并將其轉(zhuǎn)換為指向 Instance 類型的指針_ = (&ins).String() }
實(shí)際上,即使是我們?cè)趯?shí)現(xiàn) Foo 方法時(shí)的接收者是指針類型,上面 ins 調(diào)用的使用依然沒有問題。
Ins 值屬于 Instance 類型,而非 *Instance,卻能調(diào)用 Foo 方法,這是為什么呢?這其實(shí)就是 Go 編譯器提供的語法糖!
當(dāng)一個(gè)變量可變時(shí)(也就是說,該變量是一個(gè)具有地址的變量,我們可以對(duì)其進(jìn)行尋址操作),我們對(duì)類型 T 的變量直接調(diào)用 *T 方法是合法的,因?yàn)?Go 編譯器隱式地獲取了它的地址。變量可變意味著變量可尋址,因此,上文提到的 Instance{}.Foo() 會(huì)得到編譯錯(cuò)誤,就在于 Instance{} 值不能尋址。
注意 1:在 Go 中,即使變量沒有被顯式初始化,編譯器仍會(huì)為其分配內(nèi)存空間,因此變量仍然具有內(nèi)存地址。不過,由于變量沒有被初始化,它們?cè)诜峙浜髢H被賦予其類型的默認(rèn)零值,而不是初始值。當(dāng)然,這些默認(rèn)值也是存儲(chǔ)在變量分配的內(nèi)存空間中的。
例如,下面的代碼定義了一個(gè)整型變量 x,它沒有被顯式初始化,但是在分配內(nèi)存時(shí)仍然具有一個(gè)地址:
var x int fmt.Printf("%p\n", &x) // 輸出變量 x 的內(nèi)存地址
輸出結(jié)果類似于:0xc0000120a0,表明變量 x 的內(nèi)存地址已經(jīng)被分配了。但是由于變量沒有被初始化,x 的值將為整型的默認(rèn)值 0。
3、深入測(cè)試
3.1 示例
package main type B struct { Id int } func New() B { return B{} } func New2() *B { return &B{} } func (b *B) Hello() { return } func (b B) World() { return } func main() { // 方法的接收器為 *T 類型 New().Hello() // 編譯不通過 b1 := New() b1.Hello() // 編譯通過 b2 := B{} b2.Hello() // 編譯通過 (B{}).Hello() // 編譯不通過 B{}.Hello() // 編譯不通過 New2().Hello() // 編譯通過 b3 := New2() b3.Hello() // 編譯通過 b4 := &B{} // 編譯通過 b4.Hello() // 編譯通過 (&B{}).Hello() // 編譯通過 // 方法的接收器為 T 類型 New().World() // 編譯通過 b5 := New() b5.World() // 編譯通過 b6 := B{} b6.World() // 編譯通過 (B{}).World() // 編譯通過 B{}.World() // 編譯通過 New2().World() // 編譯通過 b7 := New2() b7.World() // 編譯通過 b8 := &B{} // 編譯通過 b8.World() // 編譯通過 (&B{}).World() // 編譯通過 }
輸出結(jié)果:
./main.go:25:10: cannot call pointer method on New()
./main.go:25:10: cannot take the address of New()
./main.go:33:10: cannot call pointer method on B literal
./main.go:33:10: cannot take the address of B literal
./main.go:34:8: cannot call pointer method on B literal
./main.go:34:8: cannot take the address of B literal
3.2 問題總結(jié)
假設(shè) T 類型的方法上接收器既有 T 類型的,又有 *T 指針類型的,那么就不可以在不能尋址的 T 值上調(diào)用 *T 接收器的方法
- &B{} 是指針,可尋址
- B{} 是值,不可尋址
- b := B{} b是變量,可尋址
4、總結(jié)
在 Golang 中,當(dāng)一個(gè)變量是可變的(也就是說,該變量是一個(gè)具有地址的變量,我們可以對(duì)其進(jìn)行尋址操作),我們可以通過對(duì)該變量的指針進(jìn)行方法調(diào)用來執(zhí)行對(duì)該變量的操作,否則就會(huì)導(dǎo)致編譯錯(cuò)誤。
到此這篇關(guān)于Golang中接收者方法語法糖的使用方法詳解的文章就介紹到這了,更多相關(guān)Golang語法糖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言編譯程序從后臺(tái)運(yùn)行,不出現(xiàn)dos窗口的操作
這篇文章主要介紹了Go語言編譯程序從后臺(tái)運(yùn)行,不出現(xiàn)dos窗口的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04Go語言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實(shí)現(xiàn)方法
對(duì)音視頻一直是小白,決定沉下心來,好好研究一下音視頻知識(shí),下面這篇文章主要給大家介紹了關(guān)于Go語言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實(shí)現(xiàn)方法,需要的朋友可以參考下2022-12-12Golang中interface轉(zhuǎn)string輸出打印方法
這篇文章主要給大家介紹了關(guān)于Golang中interface轉(zhuǎn)string輸出打印的相關(guān)資料,在go語言中interface轉(zhuǎn)string可以直接使用fmt提供的fmt函數(shù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02關(guān)于Golang變量初始化/類型推斷/短聲明的問題
這篇文章主要介紹了關(guān)于Golang變量初始化/類型推斷/短聲明的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02使用Go語言實(shí)現(xiàn)Yaml編碼和解碼的方法詳解
在這篇文章中,我們將介紹如何使用Go語言編寫代碼來實(shí)現(xiàn)Yaml編碼和解碼,文中有詳細(xì)的代碼示例供大家參考,對(duì)大家的學(xué)習(xí)和工作有一定的幫助,需要的朋友可以參考下2023-11-11Golang http包構(gòu)建RESTful API的實(shí)現(xiàn)
在Go語言中實(shí)現(xiàn)RESTful API可以利用標(biāo)準(zhǔn)庫net/http提供的功能,它允許你輕松地創(chuàng)建和處理HTTP請(qǐng)求,本文主要介紹了Golang http包構(gòu)建RESTful API的實(shí)現(xiàn),感興趣的可以了解一下2024-01-01本地使用Docker搭建go開發(fā)環(huán)境的全過程
最近想學(xué)習(xí)一下golang,自己之前一直把環(huán)境全部安裝在docker上,所以這次也想把golang的環(huán)境安裝在docker上,下面這篇文章主要給大家介紹了關(guān)于本地使用Docker搭建go開發(fā)環(huán)境的相關(guān)資料,需要的朋友可以參考下2022-07-07