GO 中的方法是什么?
前面我們有分享到 GO 語(yǔ)言的函數(shù),他是一等公民,那么 GO 語(yǔ)言中的方法和函數(shù)有什么區(qū)別呢?
GO 語(yǔ)言中的方法實(shí)際上和函數(shù)是類似的,只不過(guò)在函數(shù)的基礎(chǔ)上多了一個(gè)參數(shù),這個(gè)參數(shù)在 GO 語(yǔ)言中被稱為 receiver 參數(shù)
例如我們可以這么來(lái)申明一個(gè)方法:
func (xx T/*T) helloworld(入?yún)?shù)列表) (返回值列表) {
// 具體實(shí)現(xiàn)
}
demo 中的 helloworld 方法就綁定到了 receiver T 類型上,這個(gè) helloworld 方法,我們可以通過(guò)使用 T 類型或者 *T 來(lái)進(jìn)行調(diào)用,例如:
var tt T
tt.helloworld(入?yún)?shù)列表)
var pt *T
pt.helloworld(入?yún)?shù)列表)
看著還是挺簡(jiǎn)單的對(duì)嗎?對(duì)于使用的時(shí)候我們需要注意這些問(wèn)題:
- GO 語(yǔ)言中原生的類型,是不能作為上述的 receiver 的,例如 int,slice,map等等都是不行的,只能是我們自定義的類型
- 方法只能有一個(gè) receiver 參數(shù),只能綁定一個(gè),不能綁定多個(gè)
- receiver 參數(shù)類型本身不能是指針或者是接口類型,這里非常需要注意
如圖:receiver 參數(shù)類型本身不能是指針類型
[圖片上傳失敗...(image-15295b-1697727234624)]
如圖:receiver 參數(shù)類型本身不能是接口類型
[圖片上傳失敗...(image-fe9a1a-1697727234625)]
對(duì)于方法,是綁定了一個(gè) receiver 類型的參數(shù),那么這個(gè)參數(shù)是不是很像 C++ 里面的入?yún)⒛兀?/p>
func helloworld(xx T/*T, 其他入?yún)? (返回值列表){
// 具體實(shí)現(xiàn)
}
所以在 GO 語(yǔ)言里面,我們能夠明白一個(gè)方法所綁定類型的實(shí)例,實(shí)際上就是一個(gè)普通函數(shù)的第一個(gè)參數(shù)
就像這樣
func (t T) helloworld() == helloworld(t T)
func (pt *T) helloworld() == helloworld(pt *T)
那么,對(duì)于上述綁定的類型,有 T 也有 *T ,如何去選擇呢?什么時(shí)候用 T 什么時(shí)候又用 *T 呢?
此處 T 叫做值類型 , *T 叫做指針類型 ,選擇不同的類型,
- 如果選擇 T 類型,那么這是一個(gè)值傳遞類型的,在調(diào)用 helloworld 方法的時(shí)候,實(shí)際上就是傳了一個(gè) T 類型實(shí)例的副本,那么如果在 helloworld 方法中對(duì) t 做了一些變動(dòng),那么只會(huì)影響副本,對(duì)于原來(lái)的 T 類型實(shí)例,是不會(huì)有影響的
- 如果選擇了 T 類型,那么這就是一個(gè)指針類型的,調(diào)用 helloworld 方法的時(shí)候,傳的就是類型 T 的實(shí)例地址,這個(gè)時(shí)候 helloworld 內(nèi)部如果對(duì) t 做了一些變動(dòng),都會(huì)體現(xiàn)到原來(lái)的 T 的實(shí)例上*
這個(gè)理解方式實(shí)際上就和咱們普通函數(shù)中傳入的參數(shù)是一樣的,傳值和傳地址的區(qū)別
一個(gè)簡(jiǎn)單的 demo ,再來(lái)加深一下:
- 定義個(gè) TT 類型的結(jié)構(gòu),成員有 Name
- 定義 Hello1 方法綁定 TT,定義 hello2 方法綁定 *TT
- 新建一個(gè) TT 類型的變量 t,分別去調(diào)用 hello1 和 hello2
- 新建一個(gè) *TT 類型的變量 pt,分別去調(diào)用 hello1 和 hello2
[圖片上傳失敗...(image-f78f18-1697727234625)]
可以看到上述程序的結(jié)果如下:
name ==
name == hello2
name ==
name == hello2
從上述 demo 中就可以看出,實(shí)際上 GO 語(yǔ)言對(duì)于我們調(diào)用方法的時(shí)候是做了隱式轉(zhuǎn)換,無(wú)論是值類型的實(shí)例,還是指針類型的實(shí)例,都是可以去調(diào)用綁定了值類型的方法,也可以去調(diào)用綁定了指針類型的方法
只不過(guò)得到的結(jié)果,就要看具體方法是綁定的值類型,還是指針類型了
所以我們?nèi)绾稳ミx擇 receiver 的類型到此就很清楚了吧?
如果我們期望要去修改 receiver 類型的實(shí)例,那么就用指針類型,如果不期望修改,則使用值類型
當(dāng)然,如果我們是要考慮到性能,就要使用傳地址的方式較好
方法的集合
GO 里面雖然沒(méi)有類,沒(méi)有對(duì)象,沒(méi)有繼承,但是關(guān)于面向?qū)ο罄锩娴膬?nèi)容在 GO 里面完全可以使用組合的方式來(lái)進(jìn)行實(shí)現(xiàn)
所以對(duì)于GO 里面是如何組合的的基本知識(shí)我們要搞清楚,前提我們就先要弄明白方法的集合是如何玩的
在 GO 語(yǔ)言里面,我們?cè)诮o接口變量復(fù)制,使用結(jié)構(gòu)體嵌入或者接口嵌入,使用 類型別名的時(shí)候,都是會(huì)涉及到方法集合的,但是具體要看某一個(gè)實(shí)例包含哪些方法集合,我們就可以來(lái)演練一下
- 定義 ITT 接口,里面有 2 個(gè)方法 Hello1() , Hello2()
- 定義 TT 結(jié)構(gòu)體,實(shí)現(xiàn)上述接口,其中 Hello1() 綁定 TT , Hello2() 綁定 *TT
- 查看 nil 轉(zhuǎn)成 ITT 的方法集合,TT 的方法集合,*TT 的方法集合
type ITT interface{
Hello1()
Hello2()
}
type TT struct{}
func (t TT)Hello1(){}
func (pt *TT)Hello2(){}
// 打印方法集合
func GetMethodSet(i interface{}){
v := reflect.TypeOf(i)
et := v.Elem()
num := et.NumMethod()
if num == 0 {
fmt.Println(et, "methond num is 0" )
return
}
// 有方法集合
fmt.Println(et,"mthond set is :")
for i:=0;i<num;i++{
fmt.Println("---",et.Method(i).Name)
}
}
func main(){
var t TT
var pt * TT
// 先查看 nil 接口的方法集合
GetMethodSet((*ITT)(nil))
// 查看 TT 的方法集合
GetMethodSet(&t)
// 查看 *TT 的方法集合
GetMethodSet(&pt)
}
運(yùn)行結(jié)果如下:
[圖片上傳失敗...(image-4b517a-1697727234625)]
可以查看到上述案例,綁定類型為 TT 的,方法集合只有 1 個(gè),綁定類型為 *TT 的方法集合有 2 個(gè),可以看得出來(lái)
**T 類型的方法集合是包含了 T 類型和 T 類型
T 類型的方法集合是包含了 T 類型
對(duì)于 GO 語(yǔ)言里面的組合,總的來(lái)說(shuō)有三種,這個(gè)可以多多嘗試和寫(xiě) demo 練習(xí):
- 接口中嵌入接口
type ITT interface{
hello1()
}
type ITT2 interface{
hello2()
}
type ITT3 interface{
ITT
ITT2
}
接口中嵌入接口,最終的接口方法個(gè)數(shù)是取和數(shù),此處我們要注意的地方就是方法名如果重復(fù),也是可以正常得到方法集合,具體使用接口中的方法,還是要看實(shí)例是如何實(shí)現(xiàn)的
- 結(jié)構(gòu)體中嵌入接口
type ITT interface{
hello1()
hello2()
}
type ITT2 interface{
hello2()
hello3()
}
type TT struct{
ITT
ITT2
}
// 此時(shí) TT 的方法集合就存在 hello2() 是沖突的,因此此處就需要 TT 去實(shí)現(xiàn)自己的 hello2()
func (TT)hello2(){}
結(jié)構(gòu)體中嵌入接口,如果遇到同名的方法,GO 語(yǔ)言會(huì)優(yōu)先選擇結(jié)構(gòu)體自己實(shí)現(xiàn)的方法,如果結(jié)構(gòu)體自己未實(shí)現(xiàn),則會(huì)將接口中的同名方法提升到自己的結(jié)構(gòu)體中來(lái)
- 結(jié)構(gòu)體中嵌入結(jié)構(gòu)體
結(jié)構(gòu)體中嵌入結(jié)構(gòu)體,就要注意嵌入的結(jié)構(gòu)體是值類型的還是指針類型的,例如:
type TT struct{
TT1
*TT2
}
那么 TT 的方法集合是什么? *TT 的方法集合是什么?
TT 的方法集合就是按照正常值傳遞的來(lái),等于 TT1 的方法集合 加上 *TT2 的方法集合
*TT 的方法集合按照地址傳遞的來(lái),等于 *TT1的方法集合 加上 *TT2 的方法集合
那么類型別名的方法集合又是如何去查看的,有什么需要注意的地方嗎?
那么還是上述方法集合中的案例,我們分別給結(jié)構(gòu)體 TT 定一個(gè)別名,TTB,給接口類型 ITT 定義一個(gè) ITTB ,此時(shí)來(lái)查看別名的方法集合,與其原有類型的方法集合是否有差距
[圖片上傳失敗...(image-10ea33-1697727234625)]
上述程序運(yùn)行結(jié)果如下:
main.ITT mthond set is :
--- Hello1
--- Hello2
main.ITT mthond set is :
--- Hello1
--- Hello2
main.TT mthond set is :
--- Hello1
main.TT mthond set is :
--- Hello1
*main.TT mthond set is :
--- Hello1
--- Hello2
*main.TT mthond set is :
--- Hello1
--- Hello2
通過(guò)打印結(jié)果,我們可以看到,類型別名的方法集合與原有類型的方法集合是一樣的,無(wú)論是結(jié)構(gòu)體類型還是接口類型
知道 receiver 能夠調(diào)用的方法集合有哪些, 那么在具體使用的時(shí)候,就避免出錯(cuò),避免誤解,對(duì)于后續(xù)的接口組合就會(huì)理解的更加明白和清晰了
總結(jié)
本次主要分享了 GO 語(yǔ)言中方法和函數(shù)的關(guān)系,GO 語(yǔ)言中的方法集合如何查看、甄別和使用
歡迎點(diǎn)贊,關(guān)注,收藏
朋友們,你的支持和鼓勵(lì),是我堅(jiān)持分享,提高質(zhì)量的動(dòng)力
[圖片上傳失敗...(image-324050-1697727234625)]
好了,本次就到這里
技術(shù)是開(kāi)放的,我們的心態(tài),更應(yīng)是開(kāi)放的。擁抱變化,向陽(yáng)而生,努力向前行。
我是阿兵云原生,歡迎點(diǎn)贊關(guān)注收藏,下次見(jiàn)~