你還不體驗(yàn)泛型嗎?
介紹
之前有看過官方發(fā)布的一些泛型文章,但是沒動(dòng)手玩過。還有沒動(dòng)手的嗎,那么最后一班車了。
不管學(xué)什么入門先從官網(wǎng)拿例子。
這段代碼很簡(jiǎn)單,定義兩個(gè)函數(shù),計(jì)算對(duì)應(yīng)傳入的map值的和。
兩個(gè)函數(shù)最大的不同在于函數(shù)參數(shù)類型有所不同,一個(gè)map的值類型為int64,一個(gè)為float64,對(duì)應(yīng)返回參數(shù)也有所不同。
在沒有泛型的情況下,每種類型都不得不重新定義一個(gè)函數(shù)。
有人可能會(huì)說,上面的代碼你可以這樣寫在一個(gè)函數(shù)里,
你確定這真的好嗎?
泛型函數(shù)
但是,有了泛型之后,那就簡(jiǎn)單多了。
上面這段代碼中,定義了一個(gè)新函數(shù)SumIntsOrFloats,該函數(shù)聲明兩個(gè)類型參數(shù) [K comparable, V int64 | float64]。其中K指定了類型必須為可比較(即可以用作比較符 == 和 !=)。
因?yàn)?go中規(guī)定map的key必須是可比較類型。比如,我們不能這樣聲明一個(gè)map。
所以這里的K就不能使用any關(guān)鍵字。
另一個(gè)V參數(shù)指定了一個(gè)約束,該約束由int64和float64組成,使用 | 指定了聯(lián)合類型。
所以函數(shù)中m參數(shù)為map[K]V類型,K,V即為參數(shù)類型指定的類型。
假如你傳入的map值的類型為其他類型。比如下面這種就不行了。
類型約束
上面看到的是我們?cè)诜椒ㄉ蠈?duì)參數(shù)做一些約束。
當(dāng)然我們也可以直接聲明類型約束。
上面的代碼聲明了一個(gè)Number用做類型約束的接口類型。在接口里聲明int64和float64聯(lián)合類型。
在SumNumbers中如果約束類型為int64或者 float64,那么只需要使用Number類型約束即可,就不用每個(gè)不同函數(shù)寫 int64 | float64,達(dá)到代碼復(fù)用的效果。
但是如果我這樣,
我們把map中的值類型調(diào)整為自定義的otherInt64類型,otherInt64的基礎(chǔ)類型也是int64。但是,這段代碼編譯會(huì)報(bào)錯(cuò)。
原因是 int64 約束會(huì)將其限制為只能是該類型,也就是只能是int64,不能是基于此類型定義的其他類型。
如果想使用otherInt64咋么辦,很簡(jiǎn)單,只需要一個(gè)~符號(hào),
使用帶~xxtype會(huì)將其限制為基礎(chǔ)類型為xxtype的所有類型。
應(yīng)用
以上只是簡(jiǎn)單介紹了一下泛型的使用姿勢(shì),那么哪些場(chǎng)景下可以使用泛型呢?
日常開發(fā)中,像slice、map、channel的一些處理函數(shù),可能邏輯相同但是類型不同導(dǎo)致copy多個(gè)不同函數(shù),這時(shí)候可以用泛型解決。比如,
還有一些行為方面的。例如go中的排序,通過泛型,不需要每一個(gè)結(jié)構(gòu)都實(shí)現(xiàn)(Len,Less,Swap)三個(gè)方法,而是抽象出依賴于三個(gè)方法的行為。最終實(shí)現(xiàn)排序只需要依賴定義的這個(gè)抽象就行了。
其他方面的應(yīng)用可以自行體驗(yàn)。
總結(jié)
這篇文章主要帶你們體驗(yàn)下泛型的基本使用,以及對(duì)應(yīng)的類型約束,最后還簡(jiǎn)單實(shí)驗(yàn)了兩個(gè)泛型的場(chǎng)景demo,感興趣的可以自行體驗(yàn)。更多內(nèi)容,歡迎留言區(qū)域交流。
附錄
https://go.dev/doc/tutorial/generics
https://teivah.medium.com/when-to-use-generics-in-go-36d49c1aeda