最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go 泛型和非泛型代碼詳解

 更新時(shí)間:2021年10月07日 09:49:56   作者:crazstom  
Go 在 1.17 中支持泛型,但是默認(rèn)未開啟;1.18 中會正式支持泛型,下面文章內(nèi)容小編將給大家講解Go 語言中的泛型和非泛型并且附上代碼詳解,剛興趣的小伙伴請參考下面文章的具體內(nèi)容

1. 開啟泛型

在 Go1.17 版本中,可以通過:

 export GOFLAGS="-gcflags=-G=3"

或者在編譯運(yùn)行程序時(shí)加上:

 go run -gcflags=-G=3 main.go

2.無泛型代碼和泛型代碼

2.1. AddSlice

首先看現(xiàn)在沒有泛型的代碼: 

package main
 ​
 import (
   "fmt"
 )
 ​
 func AddIntSlice(input []int, diff int) []int {
   output := make([]int, 0, len(input))
   for _, item := range input {
     output = append(output, item+diff)
   }
   return output
 }
 ​
 func AddStrSlice(input []string, diff string) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item+diff)
   }
   return output
 }
 ​
 func main() {
   intSlice := []int{1, 2, 3, 4, 5, 6}
   fmt.Printf("intSlice [%+v] + 2 = [%+v]\n", intSlice, AddIntSlice(intSlice, 2))
 ​
   strSlice := []string{"hi,", "hello,", "bye,"}
   fmt.Printf("strSlice [%+v] + man = [%+v]\n", strSlice, AddStrSlice(strSlice, "man"))
 }
 //output
 //intSlice [[1 2 3 4 5 6]] + 2 = [[3 4 5 6 7 8]]
 //strSlice [[hi, hello, bye,]] + man = [[hi,man hello,man bye,man]]

上面沒有使用泛型的代碼中,對 intSlice strSlice,需要構(gòu)造兩個(gè)函數(shù)對它們進(jìn)行處理;而如果后續(xù)還有 float64、uint32 等類型就需要更多地 Add...Slice 函數(shù)。

而如果使用泛型之后,這些 Add...Slice 函數(shù)就可以合并為一個(gè)函數(shù)了,在這個(gè)函數(shù)中,對那些可以使用 + 操作符的類型進(jìn)行加操作(無論是數(shù)學(xué)的加還是字符串的連接)。

泛型代碼如下:

 package main
 ​
 import (
   "fmt"
 )
 ​
 type PlusConstraint interface {
   type int, string
 }
 ​
 func AddSlice[T PlusConstraint](input []T, diff T) []T {
   output := make([]T, 0, len(input))
   for _, item := range input {
     output = append(output, item+diff)
   }
   return output
 }
 ​
 func main() {
   intSlice := []int{1, 2, 3, 4, 5}
   fmt.Printf("intSlice [%+v] + 2 = [%v]\n", intSlice, AddSlice(intSlice, 2))
 ​
   strSlice := []string{"hi,", "hello,", "bye,"}
   fmt.Printf("strSlice [%v] + man = [%v]\n", strSlice, AddSlice(strSlice, "man"))
 }
 //output
 //intSlice [[1 2 3 4 5]] + 2 = [[3 4 5 6 7]]
 //strSlice [[hi, hello, bye,]] + man = [[hi,man hello,man bye,man]]

是不是超級簡單,但是 AddSlice 函數(shù)中引入了約束的概念,即 PlusConstraint。AddSlice 的方括號中是類型參數(shù),T 就是這個(gè)類型參數(shù)的形參,后面的 PlusConstraint 就是 T 的約束條件,意思是只有滿足約束條件的 T 類型才可以在這個(gè)函數(shù)中使用。

AddSlice 后面圓括號中的參數(shù)是常規(guī)參數(shù)也稱為非類型參數(shù),它們可以不制定具體類型(int、string 等),可以使用 T 來代替。

而在 AddSlice 中,對于 T 類型的值 item,它會將 item 和 diff 進(jìn)行 + 操作,可能是數(shù)學(xué)上的累加,也可能是字符串的連接。

那現(xiàn)在你可能要問了,T 類型就一定是支持 + 操作符的嗎,有沒有可能是一個(gè) struct 呢?

答案是:不可能。

前面說過,只有滿足約束條件的 T 才可以在 AddSlice 中使用,而約束條件就是上面的 PlusConstraint。

PlusConstraint 定義的方式和接口類型的定義是一樣的,只不過內(nèi)部多了一行:

 type int, string

這句話就是說,只有 int、string 這兩個(gè)類型才滿足這個(gè)約束,這里涉及到類型集的概念,后續(xù)會提到。

因此,有了這個(gè)約束條件,傳入到 AddSlice 的參數(shù) input diff 都是可以使用 + 操作符的。如果你的 AddSlice 函數(shù)中想傳入 float46uint64 等類型,就在 PlusConstraint 中加上這兩個(gè)類型即可。

上面的代碼中,只是對 int 和 string 兩種基礎(chǔ)類型進(jìn)行約束。實(shí)際開發(fā)中,我們可能會定義自己的類型:

 type MyInt int
 type MyStr string

那如果在 AddSlice 中使用這兩種類型可以編譯通過嗎?答案是可以的。在泛型草案中,這種情況是無法編譯通過的,需要在約束條件中添加~int | ~string,表示底層類型是 int 或 string 的類型。而在 Go1.17 中,上面的 PlusConstraint 就包括了 int、string、以及以這兩者為底層類型的類型。

 package main
 ​
 import (
   "fmt"
 )
 ​
 type MyInt int
 type MyStr string
 ​
 type PlusConstraint interface {
   type int, string
 }
 ​
 func AddSlice[T PlusConstraint](input []T, diff T) []T {
   output := make([]T, 0, len(input))
   for _, item := range input {
     output = append(output, item+diff)
 ​
   }
   return output
 ​
 }
 ​
 func main() {
   intSlice := []MyInt{1, 2, 3, 4, 5}
   fmt.Printf("intSlice [%+v] + 2 = [%v]\n", intSlice, AddSlice(intSlice, 2))
 ​
   strSlice := []MyStr{"hi,", "hello,", "bye,"}
   fmt.Printf("strSlice [%v] + man = [%v]\n", strSlice, AddSlice(strSlice, "man"))
 ​
 }
 //output
 //intSlice [[1 2 3 4 5]] + 2 = [[3 4 5 6 7]]
 //strSlice [[hi, hello, bye,]] + man = [[hi,man hello,man bye,man]]

2.2. 帶方法的約束 StringConstraint

前面說到,約束的定義和接口很像,那如果約束中有方法呢,那不就是妥妥的接口嗎?

兩者還是有區(qū)別的:

  • 接口的成員只有方法和內(nèi)嵌的接口類型
  • 約束的成員有方法、內(nèi)嵌約束類型、類型(int、string等)

看下面一個(gè)沒有使用泛型的例子:

 package main
 ​
 import (
   "fmt"
 )
 ​
 func ConvertSliceToStrSlice(input []fmt.Stringer) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item.String())
   }
   return output
 }
 ​
 type MyInt int
 ​
 func (mi MyInt) String() string {
   return fmt.Sprintf("[%d]th", mi)
 }
 func ConvertIntSliceToStrSlice(input []MyInt) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item.String())
   }
   return output
 }
 ​
 type MyStr string
 ​
 func (ms MyStr) String() string {
   return string(ms) + "!!!"
 }
 func ConvertStrSliceToStrSlice(input []MyStr) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item.String())
   }
   return output
 }
 func main() {
   intSlice := []MyInt{1, 2, 3, 4}
   // compile error, []MyInt not match []fmt.Stringer
   //fmt.Printf("%v convert %v", intSlice, ConvertSliceToStrSlice(intSlice))
 ​
   fmt.Printf("%v convertIntToStr %v \n", intSlice, ConvertIntSliceToStrSlice(intSlice))
 ​
   strSlice := []MyStr{"111", "222", "333"}
   fmt.Printf("%v convertStrToStr %v \n", strSlice, ConvertStrSliceToStrSlice(strSlice))
   // output
   //[[1]th [2]th [3]th [4]th] convertIntToStr [[1]th [2]th [3]th [4]th]
   //[111!!! 222!!! 333!!!] convertStrToStr [111!!! 222!!! 333!!!]
 }

上面代碼中,MyInt MyStr 都實(shí)現(xiàn)了 fmt.Stringer 接口,但是兩個(gè)都無法調(diào)用 ConvertSliceToStrSlice 函數(shù),因?yàn)樗娜雲(yún)⑹?[]fmt.Stringer 類型,[]MyInt 和它不匹配,這在編譯的時(shí)候就是會報(bào)錯(cuò)的,而如果我們想要把[]MyInt 轉(zhuǎn)換為 []string,就需要定義一個(gè)入?yún)閇]MyInt 的函數(shù),如 ConvertIntSliceToStrSlice;對于 []MyStr,則需要另一個(gè)函數(shù)。。。那明明兩者都實(shí)現(xiàn)了 fmt.Stringer,理論上應(yīng)該都可以通過 ConvertSliceToStrSlice 啊,這也太反人類了。

哈哈,泛型實(shí)現(xiàn)了這個(gè)功能。

 

package main
 ​
 import (
   "fmt"
 )
 ​
 type StringConstraint interface {
   String() string
 }
 ​
 func ConvertSliceToStrSlice[T StringConstraint](input []T) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item.String())
   }
   return output
 }
 ​
 type MyInt int
 ​
 func (mi MyInt) String() string {
   return fmt.Sprintf("[%d]th", mi)
 }
 ​
 type MyStr string
 ​
 func (ms MyStr) String() string {
   return string(ms) + "!!!"
 }
 func main() {
   intSlice := []MyInt{1, 2, 3, 4}
   // compile error, []MyInt not match []fmt.Stringer
   fmt.Printf("%v convert %v\n", intSlice, ConvertSliceToStrSlice(intSlice))
 ​
 ​
   strSlice := []MyStr{"111", "222", "333"}
   fmt.Printf("%v convert %v\n", strSlice, ConvertSliceToStrSlice(strSlice))
   // output
   //[[1]th [2]th [3]th [4]th] convert [[1]th [2]th [3]th [4]th]
   //[111!!! 222!!! 333!!!] convert [111!!! 222!!! 333!!!]
 }

簡單吧,在 StringConstraint 約束中定義一個(gè) String() string,這樣只要有這個(gè)方法的類型都可以作為 T 在 ConvertSliceToStrSlice 使用。在這個(gè)約束條件下,所有具有 String() string 方法的類型都可以進(jìn)行轉(zhuǎn)換,但是我們?nèi)绻氚鸭s束條件定的更加苛刻,例如只有底層類型為 int 或者 string 的類型才可以調(diào)用這個(gè)函數(shù)。 那么我們可以進(jìn)一步在 StringConstraint 中添加約束條件:

 type StringConstraint interface {
   type int, string
   String() string
 }

這樣滿足這個(gè)約束的類型集合就是底層類型是 int 或者 string,并且,具有 String() string 方法的類型。而這個(gè)類型集合就是 type int, string 的類型集合與 String() string 的類型集合的交集。具體的概念后續(xù)介紹。

這樣,MyFloat、MyUint 就無法調(diào)用 ConvertSliceToStrSlice 這個(gè)函數(shù)了。

 package main
 ​
 import (
   "fmt"
 )
 ​
 type StringConstraint interface {
   type int, string
   String() string
 }
 ​
 func ConvertSliceToStrSlice[T StringConstraint](input []T) []string {
   output := make([]string, 0, len(input))
   for _, item := range input {
     output = append(output, item.String())
   }
   return output
 }
 ​
 type MyFloat float64
 ​
 func (mf MyFloat) String() string {
   return fmt.Sprintf("%fth", mf)
 }
 ​
 type MyInt int
 ​
 func (mi MyInt) String() string {
   return fmt.Sprintf("[%d]th", mi)
 }
 ​
 type MyStr string
 ​
 func (ms MyStr) String() string {
   return string(ms) + "!!!"
 }
 func main() {
   intSlice := []MyInt{1, 2, 3, 4}
   // compile error, []MyInt not match []fmt.Stringer
   fmt.Printf("%v convert %v\n", intSlice, ConvertSliceToStrSlice(intSlice))
 ​
   strSlice := []MyStr{"111", "222", "333"}
   fmt.Printf("%v convert %v\n", strSlice, ConvertSliceToStrSlice(strSlice))
   // output
   //[[1]th [2]th [3]th [4]th] convert [[1]th [2]th [3]th [4]th]
   //[111!!! 222!!! 333!!!] convert [111!!! 222!!! 333!!!]
   floatSlice := []MyFloat{1.1, 2.2, 3.3}
   //type checking failed for main
   //prog.go2:48:44: MyFloat does not satisfy StringConstraint (MyFloat or float64 not found in int, string)
 ​
   fmt.Printf("%v convert %v\n", floatSlice, ConvertSliceToStrSlice(floatSlice))
 }

小結(jié):

總的來說,泛型可以簡化代碼的編寫,同時(shí)在編譯時(shí)進(jìn)行類型檢查,如果類型不滿足約束,就會在編譯時(shí)報(bào)錯(cuò);這樣就避免了運(yùn)行時(shí)不可控的錯(cuò)誤了。

到此這篇關(guān)于Go 泛型和非泛型代碼詳解的文章就介紹到這了,更多相關(guān)Go 泛型和非泛型代碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何使用proto組件編譯pb.go文件

    如何使用proto組件編譯pb.go文件

    這篇文章主要介紹了如何使用proto組件編譯pb.go文件的詳細(xì)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • GO中公平鎖和非公平鎖的具體使用

    GO中公平鎖和非公平鎖的具體使用

    公平鎖和非公平鎖是計(jì)算機(jī)科學(xué)中的兩種鎖機(jī)制,它們主要用于多線程編程,以控制對共享資源的訪問,本文主要介紹了GO中公平鎖和非公平鎖的具體使用,感興趣的可以了解一下
    2024-08-08
  • Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù)

    Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù)

    本文主要介紹了Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 使用go操作redis的有序集合(zset)

    使用go操作redis的有序集合(zset)

    這篇文章主要介紹了使用go操作redis的有序集合(zset),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 一文詳細(xì)談?wù)凣oLang的panic和error

    一文詳細(xì)談?wù)凣oLang的panic和error

    說是初識,并不是說第一次使用error和panic包,而是第一次特地去了解golang中的這兩個(gè)機(jī)制,下面這篇文章主要給大家介紹了關(guān)于如何通過一文詳細(xì)談?wù)凣oLang中panic和error的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • golang?gorm學(xué)習(xí)之如何指定數(shù)據(jù)表

    golang?gorm學(xué)習(xí)之如何指定數(shù)據(jù)表

    在sql中首先要指定是從哪張表中查詢,所以這篇文章小編就來帶大家一起看一下gorm是如何根據(jù)model來自動(dòng)解析表名的,感興趣的小伙伴可以了解下
    2023-08-08
  • Golang實(shí)現(xiàn)AES加密和解密的示例代碼

    Golang實(shí)現(xiàn)AES加密和解密的示例代碼

    AES( advanced encryption standard)使用相同密鑰進(jìn)行加密和解密,也就是對稱加密。本文將詳細(xì)講解Golang實(shí)現(xiàn)AES加密和解密的方法,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • Go語言中調(diào)用外部命令的方法總結(jié)

    Go語言中調(diào)用外部命令的方法總結(jié)

    在工作中,我們時(shí)不時(shí)地會需要在Go中調(diào)用外部命令。本文為大家總結(jié)了Go語言中調(diào)用外部命令的幾種姿勢,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-11-11
  • Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作

    Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作

    這篇文章主要介紹了Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • GO web 數(shù)據(jù)庫預(yù)處理的實(shí)現(xiàn)

    GO web 數(shù)據(jù)庫預(yù)處理的實(shí)現(xiàn)

    本文主要介紹了GO web 數(shù)據(jù)庫預(yù)處理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評論

翼城县| 丰县| 体育| 怀远县| 观塘区| 建水县| 灌阳县| 陇西县| 霞浦县| 六安市| 潼关县| 台北市| 武义县| 赣州市| 布拖县| 秦安县| 广元市| 六枝特区| 台北县| 古丈县| 房产| 定远县| 桃源县| 肃宁县| 北流市| 齐齐哈尔市| 凤冈县| 革吉县| 香港| 将乐县| 岳阳县| 衡南县| 左云县| 泸溪县| 华容县| 丰台区| 澄城县| 中宁县| 桃源县| 临泽县| 独山县|