<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官方出品:如何使用泛型

          共 8513字,需瀏覽 18分鐘

           ·

          2021-12-24 04:56

          目錄

          1. 前提條件

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

          3. 添加非泛型函數(shù)

          4. 添加一個泛型函數(shù)來處理多種類型

          5. 在調(diào)用泛型函數(shù)時刪除類型參數(shù)

          6. 聲明一個類型約束

          7. 結(jié)論

          8. 完整代碼

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

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

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

          你會通過以下幾個章節(jié)來進行學(xué)習(xí):

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

          備注:對其他教程,可以查看教程 備注:你同時也可以使用 "Go dev branch"模式來編輯和運行你的代碼,如果你更喜歡以這種形式的話

          前提條件


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

          安裝并使用 beta 版本

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

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

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

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

          $?go1.18beta1?download

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

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

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

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

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


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

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

          在 Linux 或者 Mac 上:

          $?cd

          在 windows 上:

          C:\>?cd?%HOMEPATH%

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

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

          $?mkdir?generics
          $?cd?generics

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

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

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

          備注:對生產(chǎn)環(huán)境,你會指定一個更符合你自己需求的 module 路徑。更多的請看依賴管理

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

          添加普通函數(shù)


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

          你將聲明兩個函數(shù)而不是一個,因為你要處理兩種不同類型的 map:一個存儲 int64 類型的值,另一個存儲 float64 類型的值。

          寫代碼

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

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

          package?main

          一個獨立的程序(相對于一個庫)總是在 main 包中。

          3、 在包的聲明下面,粘貼以下兩個函數(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
          }

          在這段代碼中,你:

          • 聲明兩個函數(shù),將一個 map 的值加在一起,并返回總和。
            • SumFloats 接收一個 map,key 為 string 類型,value 為 floa64 類型。
            • SumInt 接收一個 map,key 為 string 類型,value 為 int64 類型。

          4、 在 main.go 的頂部,包聲明的下面,粘貼以下 main 函數(shù),用來初始化兩個 map,并在調(diào)用你在上一步中聲明的函數(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))
          }

          在這段代碼中,你:

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

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

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

          package?main
          import?"fmt"

          6、 保存 main.go.

          運行代碼

          在 main.go 所在目錄下,通過命令行運行代碼

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

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

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


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

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

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

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

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

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

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

          寫代碼

          1、 在你之前寫的兩個函數(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
          }

          在這段代碼里,你:

          • 聲明了一個帶有 2 個類型參數(shù) (方括號內(nèi)) 的 SumIntsOrFloats 函數(shù),K 和 V,一個使用類型參數(shù)的參數(shù),類型為 map[K] V 的參數(shù) m。
            • 為 K 類型參數(shù)指定可比較的類型約束。事實上,針對此類情況,在 Go 里面可比較的限制是會提前聲明。它允許任何類型的值可以作為比較運算符==和!=的操作符。在 Go 里面,map 的 key 是需要可比較的。因此,將 K 聲明為可比較的是很有必要的,這樣你就可以使用 K 作為 map 變量的 key。這樣也保證了調(diào)用代碼方使用一個被允許的類型做 map 的 key。
            • 為 V 類型參數(shù)指定一個兩個類型合集的類型約束:int64 和 float64。使用|指定了 2 個類型的合集,表示約束允許這兩種類型。任何一個類型都會被編譯器認定為合法的傳參參數(shù)。指定參數(shù) m 為類型 map[K] V,其中 K 和 V 的類型已經(jīng)指定為類型參數(shù)。注意到因為 K 是可比較的類型,所以 map[K] V 是一個有效的 map 類型。如果我們沒有聲明 K 是可比較的,那么編譯器會拒絕對 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)建的每個 map。

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

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

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

          運行代碼

          在 main.go 所在目錄下,通過命令行運行代碼

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

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

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

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

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

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

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

          寫代碼

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

          在這段代碼里,你:

          • 調(diào)用泛型函數(shù),省略類型參數(shù)。

          運行代碼

          在 main.go 所在目錄下,通過命令行運行代碼

          $?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ù)和浮點數(shù)的合集定義到一個你可以重復(fù)使用的類型約束中,比如從其他的代碼,來進一步簡化這個函數(shù)。

          聲明類型約束


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

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

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

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

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

          在這段代碼里,你:

          • 聲明一個 Number interface 類型作為類型限制

          • 在 interface 內(nèi)聲明 int64 和 float64 的合集 本質(zhì)上,你是在把函數(shù)聲明中的合集移到一個新的類型約束中。這樣子,當(dāng)你想要約束一個類型參數(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
          }

          在這段代碼,你:

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

          在這段代碼里,你:

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

          運行代碼

          在 main.go 所在目錄下,通過命令行運行代碼

          $?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ù)試驗,你可以嘗試用整數(shù)約束和浮點數(shù)約束來寫 Number interface,來允許更多的數(shù)字類型。

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

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

          完整代碼


          你可以在 Go playground 運行這個代碼。在 playground 只需要點擊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

          校對:cvley



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



          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  色三区| A片黄色视频免费 | 黄色毛片一区二区 | 天天日天天干天天艹 | 中文天堂在线免费观看 |