<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 泛型簡(jiǎn)明入門(mén)教程

          共 5387字,需瀏覽 11分鐘

           ·

          2021-12-27 08:33

          閱讀本文大概需要 10?分鐘。

          大家好,我是 polarisxu。

          有泛型的 Go 版本 1.18 已經(jīng)發(fā)布了 Beta1 版本,之前陸陸續(xù)續(xù)介紹了泛型,但可能有些人還是對(duì) Go 泛型沒(méi)有完整的了解,因此有這份入門(mén)教程。

          01 準(zhǔn)備工作

          開(kāi)始學(xué)習(xí)泛型之前,你應(yīng)該安裝 Go1.18 Beta1 或之后發(fā)布的版本,建議使用 goup 等版本管理工具,當(dāng)然也可以直接通過(guò) playground 來(lái)驗(yàn)證:https://go.dev/play/?v=gotip。

          不過(guò),本教程基于本地安裝 Go1.18 Beta1 為例進(jìn)行。

          $?goup?install?1.18beta1
          Downloaded???0.0%?(????16384?/?143162528?bytes)?...
          Downloaded???5.9%?(??8404928?/?143162528?bytes)?...
          Downloaded??14.1%?(?20234096?/?143162528?bytes)?...
          Downloaded??22.3%?(?31981328?/?143162528?bytes)?...
          Downloaded??30.5%?(?43695808?/?143162528?bytes)?...
          Downloaded??38.7%?(?55443040?/?143162528?bytes)?...
          Downloaded??45.7%?(?65486352?/?143162528?bytes)?...
          Downloaded??53.9%?(?77200832?/?143162528?bytes)?...
          Downloaded??62.1%?(?88866144?/?143162528?bytes)?...
          Downloaded??70.3%?(100580624?/?143162528?bytes)?...
          Downloaded??78.4%?(112295088?/?143162528?bytes)?...
          Downloaded??85.5%?(122371168?/?143162528?bytes)?...
          Downloaded??93.7%?(134102032?/?143162528?bytes)?...
          Downloaded?100.0%?(143162528?/?143162528?bytes)
          INFO[0013]?Unpacking?/Users/xuxinhua/.go/go1.18beta1/go1.18beta1.darwin-amd64.tar.gz?...
          INFO[0020]?Success:?go1.18beta1?downloaded?in?/Users/xuxinhua/.go/go1.18beta1
          INFO[0020]?Default?Go?is?set?to?'go1.18beta1'

          驗(yàn)證是否安裝成功:

          $?go?version
          go?version?go1.18beta1?darwin/amd64

          02 創(chuàng)建項(xiàng)目

          切換到 $HOME 目錄,Linux/Mac 執(zhí)行:

          $?cd?~

          Windows 下執(zhí)行(在 C 盤(pán),基于 cmd 或 PowerShell):

          C:\>?cd?%HOMEPATH%

          然后創(chuàng)建目錄并初始化模塊:

          $?mkdir?generics
          $?cd?generics
          $?go?mod?init?github.com/polaris1119/generics
          go:?creating?new?go.mod:?module?github.com/polaris1119/generics

          其中的模塊前綴可以替換為你喜歡的名字。

          03 添加非泛型函數(shù)

          下面以 map 為例,先看非泛型如何處理,泛型又是如何處理。

          假如有兩個(gè) map,分別是 map[string]int 和 map[string]float64,編寫(xiě)函數(shù)將 map 中的 value 值相加,返回結(jié)果。因?yàn)橛袃蓚€(gè)類(lèi)型,因此編寫(xiě)兩個(gè)函數(shù)。

          func?SumInts(m?map[string]int)?int?{
          ????var?s?int
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          func?SumFloats(m?map[string]float64)?float64?{
          ????var?s?float64
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          在 main 函數(shù)中初始化兩個(gè) map 并調(diào)用上面的函數(shù)。

          func?main()?{
          ????ints?:=?map[string]int{
          ????????"first":??34,
          ????????"second":?12,
          ????}

          ????floats?:=?map[string]float64{
          ????????"first":??35.98,
          ????????"second":?26.99,
          ????}

          ????fmt.Printf("非泛型計(jì)算結(jié)果,SumInts:?%v,?SumFloats:?%v\n",?SumInts(ints),?SumFloats(floats))
          }

          運(yùn)行后,輸出結(jié)果:

          $?go?run?main.go
          非泛型計(jì)算結(jié)果,SumInts:?46,?SumFloats:?62.97

          雖然得到了想要的結(jié)果,但 SumInts 和 SumFloats 的邏輯差不多。如果將來(lái)有其他類(lèi)型,我們必須增加額外的函數(shù),代碼邏輯也類(lèi)似。

          有了泛型,只需要一個(gè)函數(shù)就可以實(shí)現(xiàn)以上兩個(gè)函數(shù)的功能,而且可以方便擴(kuò)展為支持其他相關(guān)類(lèi)型,比如 map[iint]float64 等。

          03 泛型處理多類(lèi)型

          要支持任一類(lèi)型的值,該函數(shù)將需要一種方法來(lái)聲明它支持的類(lèi)型。同時(shí),調(diào)用者需要一種方法來(lái)指定它是使用整數(shù) map 還是浮點(diǎn)數(shù) map 進(jìn)行調(diào)用,即調(diào)用時(shí)指定實(shí)際參數(shù)的類(lèi)型。

          為了支持這一點(diǎn),需要編寫(xiě)一個(gè)函數(shù),除了它的普通函數(shù)參數(shù)外,還需要聲明類(lèi)型參數(shù)。這些類(lèi)型參數(shù)使函數(shù)具有通用性,使其能夠處理不同類(lèi)型的參數(shù)。這樣,你可以使用類(lèi)型參數(shù)和普通函數(shù)參數(shù)調(diào)用該通用函數(shù),即泛型函數(shù)。

          每個(gè)類(lèi)型參數(shù)都有一個(gè)類(lèi)型約束,作為類(lèi)型參數(shù)的一種元類(lèi)型。每個(gè)類(lèi)型約束指定調(diào)用代碼可以用于相應(yīng)類(lèi)型參數(shù)的允許類(lèi)型。

          雖然類(lèi)型參數(shù)的約束通常表示一組類(lèi)型,但在編譯時(shí)類(lèi)型參數(shù)代表單個(gè)類(lèi)型——調(diào)用代碼作為類(lèi)型參數(shù)提供的類(lèi)型。如果類(lèi)型參數(shù)的約束不允許該調(diào)用者指定的類(lèi)型,則代碼將無(wú)法編譯。

          請(qǐng)記住,類(lèi)型參數(shù)必須支持泛型代碼對(duì)其執(zhí)行的所有操作。例如,函數(shù)對(duì)參數(shù)執(zhí)行加減運(yùn)算,而 string 是不支持的,因此約束中不能包含 string 類(lèi)型,否則代碼將無(wú)法編譯。

          如果沒(méi)看懂,就看具體代碼:

          func?SumIntsOrFloats[K?comparable,?V?int?|?float64](m?map[K]V)?V?{
          ????var?s?V
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          函數(shù) SumIntsOrFloats 聲明了兩種參數(shù):類(lèi)型參數(shù)和普通函數(shù)參數(shù)。其中類(lèi)型參數(shù)放在 [] 中,普通參數(shù)依然放在 () 中。(別問(wèn)類(lèi)型參數(shù)為什么不用 <>,官方給了解釋?zhuān)篽ttps://groups.google.com/g/golang-nuts/c/7t-Q2vt60J8/m/65D5xBDvBgAJ)

          該函數(shù)的類(lèi)型參數(shù)是:K comparable, V int | float64,其中 K、V 的名字不重要,分別表示某種類(lèi)型,comparable 和 int | float64 是 K、V 類(lèi)型的約束,即調(diào)用該方法時(shí),K、V 允許的類(lèi)型。comparable 是語(yǔ)言預(yù)定義的約束,官方的解釋如下:https://pkg.go.dev/builtin@master#comparable

          comparable is an interface that is implemented by all comparable types (booleans, numbers, strings, pointers, channels, interfaces, arrays of comparable types, structs whose fields are all comparable types). The comparable interface may only be used as a type parameter constraint, not as the type of a variable.

          即表示所有可比較類(lèi)型,也就是說(shuō),K 可以是任意可比較類(lèi)型。

          而 V 的類(lèi)型約束 int | float64 表示只允許是 int 或 float64,其他類(lèi)型編譯會(huì)報(bào)錯(cuò)。關(guān)于類(lèi)型約束更多內(nèi)容,可以參考我之前寫(xiě)的文章:Go1.18 類(lèi)型約束那些事

          再看該函數(shù)的普通參數(shù):m map[K]V,這表明,m 是一個(gè) map,它的 key 類(lèi)型是 K,value 類(lèi)型是 V。很顯然,這兩個(gè)是該函數(shù)「類(lèi)型參數(shù)」定義的類(lèi)型。

          泛型函數(shù)有了,該如何調(diào)用呢?

          在 main 中增加如下調(diào)用:

          fmt.Printf("泛型計(jì)算結(jié)果,Ints?結(jié)果:?Floats?結(jié)果:?%v\n",?SumIntsOrFloats[string,?int](ints),?SumIntsOrFloats[string,?float64](floats))

          同一個(gè)函數(shù),支持 map[string]int 和 map[string]float64。

          注意,我們?cè)谡{(diào)用函數(shù)和聲明函數(shù)類(lèi)型,用 [] 指定了具體的類(lèi)型,比如 SumIntsOrFloats[string, int](ints),即調(diào)用時(shí)普通參數(shù)是什么類(lèi)型通過(guò) [] 指定。很顯然,這很繁瑣,實(shí)際上 Go 會(huì)進(jìn)行類(lèi)型推斷,即編譯器會(huì)通過(guò)普通參數(shù)的類(lèi)型推導(dǎo)出「類(lèi)型參數(shù)」。不過(guò),跟 Go 中其他類(lèi)型自動(dòng)推導(dǎo)類(lèi)似,有些情況是無(wú)法自動(dòng)推導(dǎo)的,這時(shí)候必須手動(dòng)指定實(shí)際的類(lèi)型參數(shù)。

          因此,上面的調(diào)用代碼也可以簡(jiǎn)寫(xiě)為:

          fmt.Printf("泛型計(jì)算結(jié)果,Ints?結(jié)果:?Floats?結(jié)果:?%v\n",?SumIntsOrFloats(ints),?SumIntsOrFloats(floats))

          運(yùn)行后,得到如下結(jié)果:

          $?go?run?main.go
          非泛型計(jì)算結(jié)果,SumInts:?46,?SumFloats:?62.97
          泛型計(jì)算結(jié)果,Ints?結(jié)果:?46,?Floats?結(jié)果:?62.97

          04 聲明類(lèi)型約束

          上文已經(jīng)大概解釋了類(lèi)型約束,針對(duì)本文例子,解釋下類(lèi)型約束。

          上面沒(méi)有將 int | float64 定義為一個(gè)命名約束,相當(dāng)于約束字面量(或聯(lián)合類(lèi)型)。一般有兩種場(chǎng)景會(huì)單獨(dú)聲明類(lèi)型約束:

          • 約束太長(zhǎng),比如有很多類(lèi)型,直接寫(xiě)在函數(shù)中,會(huì)嚴(yán)重影響可讀性
          • 方便類(lèi)型約束重用

          將上面 V 的約束定義為單獨(dú)的類(lèi)型約束:(實(shí)際是接口,但不能作為單獨(dú)類(lèi)型使用)

          type?Number?interface{
          ??int?|?float64
          }

          基于此定義另外一個(gè)函數(shù) SumNumbers:

          func?SumNumbers[K?comparable,?V?Number](m?map[K]V)?V?{
          ????var?s?V
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          類(lèi)似的,可以這樣調(diào)用(省略了「類(lèi)型參數(shù)」):

          fmt.Printf("泛型計(jì)算結(jié)果(帶?Constraint),Ints?結(jié)果:?%v,?Floats?結(jié)果:?%v\n",
          ????SumNumbers(ints),
          ????SumNumbers(floats))

          05 總結(jié)

          泛型的內(nèi)容遠(yuǎn)不止這些,但本文作為入門(mén)教程,旨在介紹基礎(chǔ)內(nèi)容,讓大家對(duì)泛型使用有一個(gè)基本了解。本文的示例參照官方泛型教程:https://go.dev/doc/tutorial/generics。

          本文完整代碼見(jiàn) playground:https://go.dev/play/p/TwS6wda3nbv?v=gotip。




          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語(yǔ)言并創(chuàng)建了 Go 語(yǔ)言中文網(wǎng)!著有《Go語(yǔ)言編程之旅》、開(kāi)源圖書(shū)《Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)》等。


          堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長(zhǎng)!也歡迎加我微信好友交流:gopherstudio

          瀏覽 107
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人草在线视频观看 | 亚洲AV色香蕉一区二区三区 | 小黄片下载免费视频 | 操黑丝在线 | 殴美成人性爱大片免费看 |