91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解Go泛型和非泛型代碼

發布時間:2021-10-08 09:08:21 來源:億速云 閱讀:118 作者:iii 欄目:開發技術

本篇內容主要講解“如何理解Go泛型和非泛型代碼”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何理解Go泛型和非泛型代碼”吧!

目錄
  • 1. 開啟泛型

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

    • 2.1. AddSlice

    • 2.2. 帶方法的約束 StringConstraint

1. 開啟泛型

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

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

或者在編譯運行程序時加上:

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

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

2.1. AddSlice

首先看現在沒有泛型的代碼: 

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,需要構造兩個函數對它們進行處理;而如果后續還有 float64uint32 等類型就需要更多地 Add...Slice 函數。

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

泛型代碼如下:

 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 函數中引入了約束的概念,即 PlusConstraintAddSlice 的方括號中是類型參數,T 就是這個類型參數的形參,后面的 PlusConstraint 就是 T 的約束條件,意思是只有滿足約束條件的 T 類型才可以在這個函數中使用。

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

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

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

答案是:不可能。

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

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

 type int, string

這句話就是說,只有 intstring 這兩個類型才滿足這個約束,這里涉及到類型集的概念,后續會提到。

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

上面的代碼中,只是對 int 和 string 兩種基礎類型進行約束。實際開發中,我們可能會定義自己的類型:

 type MyInt int
 type MyStr string

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

 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

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

兩者還是有區別的:

  • 接口的成員只有方法和內嵌的接口類型

  • 約束的成員有方法、內嵌約束類型、類型(int、string等)


看下面一個沒有使用泛型的例子:

 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 都實現了 fmt.Stringer 接口,但是兩個都無法調用 ConvertSliceToStrSlice 函數,因為它的入參是 []fmt.Stringer 類型,[]MyInt 和它不匹配,這在編譯的時候就是會報錯的,而如果我們想要把[]MyInt 轉換為 []string,就需要定義一個入參為[]MyInt 的函數,如 ConvertIntSliceToStrSlice;對于 []MyStr,則需要另一個函數。。。那明明兩者都實現了 fmt.Stringer,理論上應該都可以通過 ConvertSliceToStrSlice 啊,這也太反人類了。

哈哈,泛型實現了這個功能。

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 約束中定義一個 String() string,這樣只要有這個方法的類型都可以作為 T 在 ConvertSliceToStrSlice 使用。在這個約束條件下,所有具有 String() string 方法的類型都可以進行轉換,但是我們如果想把約束條件定的更加苛刻,例如只有底層類型為 int 或者 string 的類型才可以調用這個函數。 那么我們可以進一步在 StringConstraint 中添加約束條件:

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

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

這樣,MyFloatMyUint 就無法調用 ConvertSliceToStrSlice 這個函數了。

 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))
 }

到此,相信大家對“如何理解Go泛型和非泛型代碼”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

go
AI

苍山县| 治县。| 延寿县| 玉屏| 茶陵县| 江川县| 邢台市| 克山县| 逊克县| 阜城县| 扶余县| 盐亭县| 怀化市| 乐平市| 哈巴河县| 都江堰市| 那坡县| 石林| 利川市| 额济纳旗| 来宾市| 中宁县| 仙居县| 阳新县| 鄂托克旗| 郁南县| 寻甸| 肃宁县| 上蔡县| 大足县| 湾仔区| 和静县| 南投市| 共和县| 喀什市| 宁陕县| 沿河| 田阳县| 吉水县| 偏关县| 台安县|