<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泛型的基本使用

          共 5144字,需瀏覽 11分鐘

           ·

          2021-09-30 17:11

          閱讀本文大概需要 9 分鐘。

          泛型,是 Go 語言多年來最令人興奮和根本性的變化之一。沒有泛型,很多人以此「鄙視」Go 語言。當(dāng)然,也有人覺得根本不需要泛型。有泛型,不代表你一定要用。平心而論,有些場(chǎng)景下,泛型還是很有必要和幫助的。

          現(xiàn)在已經(jīng)確認(rèn),Go1.18 正式包含泛型(Go1.17 已經(jīng)可以試用,只是默認(rèn)不支持,見之前的文章:揚(yáng)眉吐氣:剛剛,Go 已經(jīng)默認(rèn)支持泛型了)。

          不過,不少人對(duì)泛型還是迷迷糊糊的。本文就嘗試用簡(jiǎn)單的術(shù)語解釋泛型相關(guān)的內(nèi)容。

          01 什么是泛型

          Go 是一門強(qiáng)類型語言,意味著程序中的每個(gè)變量和值都有某種特定的類型,例如intstring 等。在函數(shù)簽名中,我們需要對(duì)參數(shù)和返回值指定類型,如下所示:

          func Add(a, b int) int

          參數(shù) ab 的類型是 int,返回值類型也是 int,結(jié)果是 ab 的和。

          如果現(xiàn)在需要一個(gè)對(duì)兩個(gè) float64 求和的函數(shù),怎么辦?

          大概率會(huì)出現(xiàn)類似這樣的函數(shù):

          func AddFloat(a, b float64) float64

          如果有更多其他的類型(比如字符串相加),可能需要寫更多的對(duì)應(yīng)版本函數(shù),很不方便,也很繁瑣,一堆復(fù)制粘貼的代碼。

          02 Go 中的泛型函數(shù)

          如果有了泛型,上面的問題怎么解決呢?只需要一個(gè)函數(shù)就搞定:

          func Add[T any](a, b T) T

          是不是很簡(jiǎn)單?不過看著有點(diǎn)暈?稍微解釋下:

          • Add 后面的 [T any],T 表示類型的標(biāo)識(shí),any 表示 T 可以是任意類型
          • a、b 和返回值的類型 T 和前面的 T 是同一個(gè)類型
          • 為什么用 [],而不是其他語言中的 <>,官方有過解釋,大概就是 <> 會(huì)有歧義。曾經(jīng)計(jì)劃使用 (),因?yàn)樘菀谆煜詈笫褂昧?[]

          這樣就表示,a、b 和返回值可以是任意類型,但它們的類型是同一個(gè)。那具體是什么類型如何確定呢?根據(jù)調(diào)用時(shí)的實(shí)際參數(shù)決定。因此,我們現(xiàn)在可以這么使用:

          Add(12)
          Add(2.13.2)

          不過,這時(shí)候代碼會(huì)報(bào)錯(cuò)。你可以本地用 Go1.17 啟用泛型的方式試驗(yàn),也可以使用 gotip 版本,亦或直接訪問這里試驗(yàn):https://go2goplay.golang.org/p/vTHnUA_8vOI

          package main

          import (
           "fmt"
          )

          func Add[T any](a, b T) T {
           return a + b
          }

          func main() {
           fmt.Println(Add(12))
           fmt.Println(Add(2.13.2))
          }

          運(yùn)行會(huì)報(bào)錯(cuò):

          type checking failed for main
          prog.go2:8:9: invalid operation: operator + not defined for a (variable of type parameter type T)

          為什么?請(qǐng)看下文。

          03 約束

          很顯然,并非所有類型都支持加法操作。因此我們需要給出約束,指定可以進(jìn)行加法操作的類型。

          上面代碼中,我們對(duì)類型 T 使用的是 any,相當(dāng)于沒有進(jìn)行任何約束。現(xiàn)在我們給一個(gè)約束:

          type Addable interface {
           type intint8int16int32int64uintuint8uint16uint32uint64uintptrfloat32float64complex64complex128string
          }

          這是新語法,叫做類型列表(type list)。

          首先,Addable 重用了接口語法,即 interface 關(guān)鍵字,表示約束,具體約束的類型通過 type 指定,多個(gè)用逗號(hào)分隔。

          現(xiàn)在 Add 函數(shù)中 T 的約束從 any 改為 Addable:

          func Add[T Addable](a, b T) T {
           return a + b
          }

          現(xiàn)在再次運(yùn)行:https://go2goplay.golang.org/p/4J52QmGrc-M,發(fā)現(xiàn)正常了。而且還支持字符串、復(fù)數(shù)等:

          Add("polaris""xu")

          可見,約束可以是任意接口類型。(any 相當(dāng)于空接口)

          還有另外一種場(chǎng)景:可比較。比如 map 中的 key 要求是可比較的。比如下面的代碼:

          func findFunc[T any](a []T, v T) int {
           for i, e := range a {
            if  e == v {
                return i
              }
           }
           return -1
           }

          T 的約束是任意類型,而實(shí)際上并非所有類型都是可比較的。怎么辦?我們當(dāng)然可以向上面 Addable 一樣定義一個(gè)約束,但為了方便,Go 內(nèi)置提供了一個(gè) comparable 約束,表示可比較的。參考下面代碼:

          package main

          func findFunc[T comparable](a []T, v T) int {
           for i, e := range a {
            if e == v {
             return i
            }
           }
           return -1
          }

          func main() {
           print(findFunc([]int{123456}, 5))
          }

          04 constraints 包

          寫泛型代碼時(shí),約束挺常見。再看一個(gè)例子,從切片中找出最大值:

          func Max[T any](input []T) (max T) {
              for _, v := range input {
                  if v > max {
                      max = v
                  }
              }
              return
          }

          但運(yùn)行會(huì)報(bào)錯(cuò):

          fmt.Println(Max([]int{14210}))
          // cannot compare v > max (operator > not defined for T)

          這時(shí),我們自然想到使用上面 Add 函數(shù)類似的辦法,自定義一個(gè)約束:Ordered,把可能的類型都列上。

          type Ordered interface {
              type intint8int16int32int64uintuint8uint16uint32uint64uintptrfloat32float64string
          }

          因?yàn)檫@樣的需求挺常見的,為了方面,官方提供了一個(gè)新包:constraints,預(yù)定義了一些約束,具體查看:https://github.com/golang/go/issues/45458。

          有了它,不需要自定義這個(gè) Ordered 約束,而是使用 constraints 包中的,即:

          func Max[T constraints.Ordered](input []T) (max T)

          05 泛型類型

          上面,我們介紹了泛型函數(shù):即函數(shù)可以接受任意類型。注意和 interface{} 這樣的任意類型區(qū)分開,泛型中的類型,在函數(shù)內(nèi)部并不需要做任何類型斷言和反射的工作,在編譯期就可以確定具體的類型。

          我們知道,Go 支持自定義類型,比如標(biāo)準(zhǔn)庫 sort 包中的 IntSlice:

          type IntSlice []int

          此外,還有 StringSliceFloat64Slice 等,一堆重復(fù)代碼。如果我們能夠定義泛型類型,就不需要定義這么多不同的類型了。比如:

          type Slice[T any] []T

          能看懂吧。

          在使用時(shí),針對(duì) int 類型,就是這樣:

          x := Slice[int]{123}

          如果作為函數(shù)參數(shù),這么使用:

          func PrintSlice[T any](b Slice[T])

          如果為這個(gè)類型定義方法,則是這樣:

          func (b Slice[T]) Print()

          也就是說,Slice[T] 作為整體存在。

          當(dāng)然,泛型類型也可以做類型約束,而不是 any 類型:

          type Slice[T comparable] []T

          06 總結(jié)

          通過本文的講解,相信你對(duì) Go 泛型有了一個(gè)基本的掌握。

          Go1.18 會(huì)包含不少泛型相關(guān)的標(biāo)準(zhǔn)庫,包括對(duì)現(xiàn)有標(biāo)準(zhǔn)庫的泛型支持,這是目前 Go 官方的重要工作。

          今天開一個(gè)頭,后續(xù)會(huì)不斷分享 Go 泛型更多的內(nèi)容,大家一起提前掌握 Go 泛型。




          往期推薦


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


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


          瀏覽 35
          點(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 | 欧美三级韩国三级日本三斤在线观看en | 爆操乱伦 | 乱能综合网 | 久操啊|