<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』Go 官方出品泛型教程:如何開始使用泛型

          共 8537字,需瀏覽 18分鐘

           ·

          2022-02-21 20:31

          備注:這是一個(gè)beta 版本的內(nèi)容

          這個(gè)教程介紹了Go泛型的基礎(chǔ)概念。通過泛型,你可以聲明并使用函數(shù)或者是類型,那些用于調(diào)用代碼時(shí)參數(shù)需要兼容多個(gè)不同類型的情況。

          在這個(gè)教程里,你會(huì)聲明兩個(gè)普通的函數(shù),然后復(fù)制一份相同的邏輯到一個(gè)泛型的方法里。

          你會(huì)通過以下幾個(gè)章節(jié)來進(jìn)行學(xué)習(xí):

          • 為你的代碼創(chuàng)建一個(gè)文件夾;
          • 添加非泛型函數(shù);
          • 添加一個(gè)泛型函數(shù)來處理多種類型;
          • 在調(diào)用泛型函數(shù)時(shí)刪除類型參數(shù);
          • 聲明一個(gè)類型約束。

          備注:對(duì)其他教程,可以查看教程(https://gocn.vip/topics/20885)?

          備注:你同時(shí)也可以使用 “Go dev branch”(https://gocn.vip/topics/20885)模式來編輯和運(yùn)行你的代碼,如果你更喜歡以這種形式的話



          前提條件



          • 安裝Go 1.18 Beta 1或者更新的版本。對(duì)安裝流程,請(qǐng)看安裝并使用beta版本。
          • 代碼編輯器。任何你順手的代碼編輯器。
          • 一個(gè)命令終端。Go在任何終端工具上都很好使用,比如Linux 、Mac、PowerShell或者Windows上的cmd。

          安裝并使用beta版本

          這個(gè)教程需要Beta 1的泛型特性。安裝beta版本,需要通過下面幾個(gè)步驟:

          1、 執(zhí)行下面的指令安裝beta版本

          ?$?go?install?golang.org/dl/go1.18beta1@latest

          2、 執(zhí)行下面的指令下載更新

          ?$?go1.18beta1?download

          3、用beta版本執(zhí)行g(shù)o命令,而不是Go的發(fā)布版本(如果你本地有安裝的話)

          你可以使用beta版本名稱或者把beta重命名成別的名稱來執(zhí)行命令。

          • 使用beta版本名稱,你可以通過go1.18beta1來執(zhí)行指令而不是go:
          $?go1.18beta1?version
          • 通過對(duì)beta版本名稱重命名,你可以簡(jiǎn)化指令:shell

          $ alias go=go1.18beta1

          $ go version

          在這個(gè)教程中將假設(shè)你已經(jīng)對(duì)beta版本名稱進(jìn)行了重命名。



          為你的代碼創(chuàng)建一個(gè)文件夾



          在一開始,先給你要寫的代碼創(chuàng)建一個(gè)文件夾

          1、 打開一個(gè)命令提示符并切換到/home文件夾

          在Linux或者M(jìn)ac上:

          $?cd

          在windows上:

          ?C:\>?cd?%HOMEPATH%

          在接下去的教程里面會(huì)用$來代表提示符。指令在windows上也適用。

          2、 在命令提示符下,為你的代碼創(chuàng)建一個(gè)名為generics的目錄

          ?$?mkdir?generics
          ???$?cd?generics

          3、 創(chuàng)建一個(gè)module來存放你的代碼

          執(zhí)行go mod init指令,參數(shù)為你新代碼的module路徑

          ?$?go?mod?init?example/generics
          ???go:?creating?new?go.mod:?module?example/generics

          備注:對(duì)生產(chǎn)環(huán)境,你會(huì)指定一個(gè)更符合你自己需求的module路徑。更多的請(qǐng)看依賴管理(https://go.dev/doc/modules/managing-dependencies)

          接下來,你會(huì)增加一些簡(jiǎn)單的和maps相關(guān)的代碼。



          添加普通函數(shù)



          在這一步中,你將添加兩個(gè)函數(shù),每個(gè)函數(shù)都會(huì)累加 map 中的值 ,并返回總和。

          你將聲明兩個(gè)函數(shù)而不是一個(gè),因?yàn)槟阋幚韮煞N不同類型的map:一個(gè)存儲(chǔ)int64類型的值,另一個(gè)存儲(chǔ)float64類型的值。

          寫代碼

          1、 用你的文本編輯器,在generics文件夾里面創(chuàng)建一個(gè)叫main.go的文件。你將會(huì)在這個(gè)文件內(nèi)寫你的Go代碼。

          2、 到main.go文件的上方,粘貼如下的包的聲明。

          ?package?main

          一個(gè)獨(dú)立的程序(相對(duì)于一個(gè)庫(kù))總是在main包中。

          3、 在包的聲明下面,粘貼以下兩個(gè)函數(shù)的聲明。

          ?//?SumInts?adds?together?the?values?of?m.
          ???func?SumInts(m?map[string]int64)?int64?{
          ???????var?s?int64
          ???????for?_,?v?:=?range?m?{
          ???????????s?+=?v
          ???????}
          ???????return?s
          ???}
          ???
          ???//?SumFloats?adds?together?the?values?of?m.
          ???func?SumFloats(m?map[string]float64)?float64?{
          ???????var?s?float64
          ???????for?_,?v?:=?range?m?{
          ???????????s?+=?v
          ???????}
          ???????return?s
          ???}

          在這段代碼中,你:

          • 聲明兩個(gè)函數(shù),將一個(gè)map的值加在一起,并返回總和。
            • SumFloats接收一個(gè)map,key為string類型,value為floa64類型。
            • SumInt接收一個(gè)map,key為string類型,value為int64類型。
          4、 在main.go的頂部,包聲明的下面,粘貼以下main函數(shù),用來初始化兩個(gè)map,并在調(diào)用你在上一步中聲明的函數(shù)時(shí)將它們作為參數(shù)。
          func?main()?{
          ???//?Initialize?a?map?for?the?integer?values
          ???ints?:=?map[string]int64{
          ???????"first":?34,
          ???????"second":?12,
          ???}
          ???
          ???//?Initialize?a?map?for?the?float?values
          ???floats?:=?map[string]float64{
          ???????"first":?35.98,
          ???????"second":?26.99,
          ???}
          ???
          ???fmt.Printf("Non-Generic?Sums:?%v?and?%v\n",
          ???????SumInts(ints),
          ???????SumFloats(floats))
          ???}

          在這段代碼中,你:

          • 初始化一個(gè)key為string,value為float64的map和一個(gè)key為string,value為int64的map,各有2條數(shù)據(jù);
          • 調(diào)用之前聲明的兩個(gè)方法來獲取每個(gè)map的值的總和;
          • 打印結(jié)果。

          5、 靠近main.go頂部,僅在包聲明的下方,導(dǎo)入你剛剛寫的代碼所需要引用的包。

          第一行代碼應(yīng)該看起來如下所示:

          package?main
          ???import?"fmt"

          6、 保存main.go.

          運(yùn)行代碼

          在main.go所在目錄下,通過命令行運(yùn)行代碼

          $?go?run?.
          Non-Generic?Sums:?46?and?62.97

          有了泛型,你可以只寫一個(gè)函數(shù)而不是兩個(gè)。接下來,你將為maps添加一個(gè)泛型函數(shù),來允許接收整數(shù)類型或者是浮點(diǎn)數(shù)類型。



          添加泛型函數(shù)處理多種類型



          在這一節(jié),你將會(huì)添加一個(gè)泛型函數(shù)來接收一個(gè)map,可能值是整數(shù)類型或者浮點(diǎn)數(shù)類型的map,有效地用一個(gè)函數(shù)替換掉你剛才寫的2個(gè)函數(shù)。

          為了支持不同類型的值,這個(gè)函數(shù)需要有一個(gè)方法來聲明它所支持的類型。另一方面,調(diào)用代碼將需要一種方法來指定它是用整數(shù)還是浮點(diǎn)數(shù)來調(diào)用。

          為了實(shí)現(xiàn)上面的描述,你將會(huì)聲明一個(gè)除了有普通函數(shù)參數(shù),還有類型參數(shù)的函數(shù)。這個(gè)類型參數(shù)實(shí)現(xiàn)了函數(shù)的通用性,使得它可以處理多個(gè)不同的類型。你將會(huì)用類型參數(shù)和普通函數(shù)參數(shù)來調(diào)用這個(gè)泛型函數(shù)。

          每個(gè)類型參數(shù)都有一個(gè)類型約束,類似于每個(gè)類型參數(shù)的meta-type。每個(gè)類型約束都指定了調(diào)用代碼時(shí)每個(gè)對(duì)應(yīng)輸入?yún)?shù)的可允許的類型。

          雖然類型參數(shù)的約束通常代表某些類型,但是在編譯的時(shí)候類型參數(shù)只代表一個(gè)類型-在調(diào)用代碼時(shí)作為類型參數(shù)。如果類型參數(shù)的類型不被類型參數(shù)的約束所允許,代碼則無法編譯。

          需要記住的是類型參數(shù)必須滿足泛型代碼對(duì)它的所有的操作。舉個(gè)例子,如果你的代碼嘗試去做一些string的操作(比如索引),而這個(gè)類型參數(shù)包含數(shù)字的類型,那代碼是無法編譯的。

          在下面你要編寫的代碼里,你會(huì)使用允許整數(shù)或者浮點(diǎn)數(shù)類型的限制。

          寫代碼?

          1、 在你之前寫的兩個(gè)函數(shù)的下方,粘貼下面的泛型函數(shù)

          ?//?SumIntsOrFloats?sums?the?values?of?map?m.?It?supports?both?int64?and?float64
          ???//?as?types?for?map?values.
          ???func?SumIntsOrFloats[K?comparable,?V?int64?|?float64](m?map[K]V)?V?{
          ???????var?s?V
          ???????for?_,?v?:=?range?m?{
          ???????????s?+=?v
          ???????}
          ???????return?s
          ???}

          在這段代碼里,你:

          • 聲明了一個(gè)帶有2個(gè)類型參數(shù)(方括號(hào)內(nèi))的SumIntsOrFloats函數(shù),K和V,一個(gè)使用類型參數(shù)的參數(shù),類型為map[K]V的參數(shù)m。
            • 為K類型參數(shù)指定可比較的類型約束。事實(shí)上,針對(duì)此類情況,在Go里面可比較的限制是會(huì)提前聲明。它允許任何類型的值可以作為比較運(yùn)算符==和!=的操作符。在Go里面,map的key是需要可比較的。因此,將K聲明為可比較的是很有必要的,這樣你就可以使用K作為map變量的key。這樣也保證了調(diào)用代碼方使用一個(gè)被允許的類型做map的key。
            • 為V類型參數(shù)指定一個(gè)兩個(gè)類型合集的類型約束:int64和float64。使用|指定了2個(gè)類型的合集,表示約束允許這兩種類型。任何一個(gè)類型都會(huì)被編譯器認(rèn)定為合法的傳參參數(shù)。
            • 指定參數(shù)m為類型map[K]V,其中K和V的類型已經(jīng)指定為類型參數(shù)。注意到因?yàn)镵是可比較的類型,所以map[K]V是一個(gè)有效的map類型。如果我們沒有聲明K是可比較的,那么編譯器會(huì)拒絕對(duì)map[K]V的引用。
          2、 在main.go里,在你現(xiàn)在的代碼下方,粘貼如下代碼
          ?fmt.Printf("Generic?Sums:?%v?and?%v\n",
          ???????SumIntsOrFloats[string,?int64](ints),
          ???????SumIntsOrFloats[string,?float64](floats))

          在這段代碼里,你:

          • 調(diào)用你剛才聲明的泛型函數(shù),傳遞你創(chuàng)建的每個(gè)map。

          • 指定類型參數(shù)-在方括號(hào)內(nèi)的類型名稱-來明確你所調(diào)用的函數(shù)中應(yīng)該用哪些類型來替代類型參數(shù)。

            你將會(huì)在下一節(jié)看到,你通??梢栽诤瘮?shù)調(diào)用時(shí)省略類型參數(shù)。Go通常可以從代碼里推斷出來。

          • 打印函數(shù)返回的總和。

          運(yùn)行代碼

          在main.go所在目錄下,通過命令行運(yùn)行代碼

          $?go?run?.
          Non-Generic?Sums:?46?and?62.97
          Generic?Sums:?46?and?62.97

          為了運(yùn)行你的代碼,在每次調(diào)用的時(shí)候,編譯器都會(huì)用該調(diào)用中指定的具體類型替換類型參數(shù)。

          在調(diào)用你寫的泛型函數(shù)時(shí),你指定了類型參數(shù)來告訴編譯器用什么類型來替換函數(shù)的類型參數(shù)。正如你將在下一節(jié)所看到的,在許多情況下,你可以省略這些類型參數(shù),因?yàn)榫幾g器可以推斷出它們。



          當(dāng)調(diào)用泛型函數(shù)時(shí)移除類型參數(shù)



          在這一節(jié),你會(huì)添加一個(gè)泛型函數(shù)調(diào)用的修改版本,通過一個(gè)小的改變來簡(jiǎn)化代碼。在這個(gè)例子里你將移除掉不需要的類型參數(shù)。

          當(dāng)Go編譯器可以推斷出你要使用的類型時(shí),你可以在調(diào)用代碼中省略類型參數(shù)。編譯器從函數(shù)參數(shù)的類型中推斷出類型參數(shù)。

          注意這不是每次都可行的。舉個(gè)例子,如果你需要調(diào)用一個(gè)沒有參數(shù)的泛型函數(shù),那么你需要在調(diào)用函數(shù)時(shí)帶上類型參數(shù)。

          寫代碼
          • 在main.go的代碼下方,粘貼下面的代碼。
          fmt.Printf("Generic?Sums,?type?parameters?inferred:?%v?and?%v\n",
          ??????SumIntsOrFloats(ints),
          ??????SumIntsOrFloats(floats))

          在這段代碼里,你:

          • 調(diào)用泛型函數(shù),省略類型參數(shù)。
          運(yùn)行代碼

          在main.go所在目錄下,通過命令行運(yùn)行代碼

          $?go?run?.
          Non-Generic?Sums:?46?and?62.97
          Generic?Sums:?46?and?62.97
          Generic?Sums,?type?parameters?inferred:?46?and?62.97

          接下來,你將通過把整數(shù)和浮點(diǎn)數(shù)的合集定義到一個(gè)你可以重復(fù)使用的類型約束中,比如從其他的代碼,來進(jìn)一步簡(jiǎn)化這個(gè)函數(shù)。



          聲明類型約束



          在最后一節(jié)中,你將把你先前定義的約束移到它自己的interface中,這樣你就可以在多個(gè)地方重復(fù)使用它。以這種方式聲明約束有助于簡(jiǎn)化代碼,尤其當(dāng)一個(gè)約束越來越復(fù)雜的時(shí)候。

          你將類型參數(shù)定義為一個(gè)interface。約束允許任何類型實(shí)現(xiàn)這個(gè)interface。舉個(gè)例子,如果你定義了一個(gè)有三個(gè)方法的類型參數(shù)interface,然后用它作為一個(gè)泛型函數(shù)的類型參數(shù),那么調(diào)用這個(gè)函數(shù)的類型參數(shù)必須實(shí)現(xiàn)這些方法。

          你將在本節(jié)中看到,約束interface也可以指代特定的類型。

          寫代碼

          1、 在main函數(shù)上面,緊接著import下方,粘貼如下代碼來定義類型約束。

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

          在這段代碼里,你:

          • 聲明一個(gè)Number interface類型作為類型限制

          • 在interface內(nèi)聲明int64和float64的合集

            本質(zhì)上,你是在把函數(shù)聲明中的合集移到一個(gè)新的類型約束中。這樣子,當(dāng)你想要約束一個(gè)類型參數(shù)為int64或者float64,你可以使用Number interface而不是寫 int64 | float64。

          2、 在你已寫好的函數(shù)下方,粘貼如下泛型函數(shù),SumNumbers。

          ?//?SumNumbers?sums?the?values?of?map?m.?Its?supports?both?integers
          ???//?and?floats?as?map?values.
          ???func?SumNumbers[K?comparable,?V?Number](m?map[K]V)?V?{
          ???????var?s?V
          ???????for?_,?v?:=?range?m?{
          ???????????s?+=?v
          ???????}
          ???????return?s
          ???}

          在這段代碼,你:

          • 聲明一個(gè)泛型函數(shù),其邏輯與你之前聲明的泛型函數(shù)相同,但是是使用新的interface類型作為類型參數(shù)而不是合集。和之前一樣,你使用類型參數(shù)作為參數(shù)和返回類型。
          3、 在main.go,在你已寫完的代碼下方,粘貼如下代碼。
          ?fmt.Printf("Generic?Sums?with?Constraint:?%v?and?%v\n",
          ???????SumNumbers(ints),
          ???????SumNumbers(floats))

          在這段代碼里,你:

          • 每個(gè)map依次調(diào)用SumNumbers,并打印數(shù)值的總和。
          • 與上一節(jié)一樣,你可以在調(diào)用泛型函數(shù)時(shí)省略類型參數(shù)(方括號(hào)中的類型名稱)。Go編譯器可以從其他參數(shù)中推斷出類型參數(shù)。
          運(yùn)行代碼

          在main.go所在目錄下,通過命令行運(yùn)行代碼

          $?go?run?.
          Non-Generic?Sums:?46?and?62.97
          Generic?Sums:?46?and?62.97
          Generic?Sums,?type?parameters?inferred:?46?and?62.97
          Generic?Sums?with?Constraint:?46?and?62.97



          總結(jié)



          完美結(jié)束!你剛才已經(jīng)給你自己介紹了Go的泛型。

          如果你想繼續(xù)試驗(yàn),你可以嘗試用整數(shù)約束和浮點(diǎn)數(shù)約束來寫Number interface,來允許更多的數(shù)字類型。

          建議閱讀的相關(guān)文章:

          • Go Tour 是一個(gè)很好的,手把手教Go基礎(chǔ)的介紹。
          • 你可以在 Effective Go(https://go.dev/doc/effective_go) 和 How to write Go code(https://go.dev/doc/code) 中找到非常實(shí)用的GO的練習(xí)。



          完整代碼



          你可以在Go playground運(yùn)行這個(gè)代碼。在playground只需要點(diǎn)擊Run按鈕即可。

          package?main

          import?"fmt"

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

          func?main()?{
          ????//?Initialize?a?map?for?the?integer?values
          ????ints?:=?map[string]int64{
          ????????"first":?34,
          ????????"second":?12,
          ????}

          ????//?Initialize?a?map?for?the?float?values
          ????floats?:=?map[string]float64{
          ????????"first":?35.98,
          ????????"second":?26.99,
          ????}

          ????fmt.Printf("Non-Generic?Sums:?%v?and?%v\n",
          ????????SumInts(ints),
          ????????SumFloats(floats))

          ????fmt.Printf("Generic?Sums:?%v?and?%v\n",
          ????????SumIntsOrFloats[string,?int64](ints),
          ????????SumIntsOrFloats[string,?float64](floats))

          ????fmt.Printf("Generic?Sums,?type?parameters?inferred:?%v?and?%v\n",
          ????????SumIntsOrFloats(ints),
          ????????SumIntsOrFloats(floats))

          ????fmt.Printf("Generic?Sums?with?Constraint:?%v?and?%v\n",
          ????????SumNumbers(ints),
          ????????SumNumbers(floats))
          }

          //?SumInts?adds?together?the?values?of?m.
          func?SumInts(m?map[string]int64)?int64?{
          ????var?s?int64
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          //?SumFloats?adds?together?the?values?of?m.
          func?SumFloats(m?map[string]float64)?float64?{
          ????var?s?float64
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          //?SumIntsOrFloats?sums?the?values?of?map?m.?It?supports?both?floats?and?integers
          //?as?map?values.
          func?SumIntsOrFloats[K?comparable,?V?int64?|?float64](m?map[K]V)?V?{
          ????var?s?V
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }

          //?SumNumbers?sums?the?values?of?map?m.?Its?supports?both?integers
          //?and?floats?as?map?values.
          func?SumNumbers[K?comparable,?V?Number](m?map[K]V)?V?{
          ????var?s?V
          ????for?_,?v?:=?range?m?{
          ????????s?+=?v
          ????}
          ????return?s
          }



          原文信息


          原文地址:

          https://go.dev/doc/tutorial/generics


          原文作者:

          go.dev


          本文永久鏈接:https://github.com/gocn/translator/

          blob/master/2021/w49_Tutorial_Getting_started_

          with_generics.md


          譯者:zxmfke


          校對(duì):cvley



          想要了解關(guān)于 Go 的更多資訊,還可以通過掃描的方式,進(jìn)群一起探討哦~



          瀏覽 45
          點(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>
                  国产乱伦中文 | 丝袜黄片| 久热久热在线 | 欧美操逼片 | 黑几巴视频久久 |