自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go泛型:提前掌握Go泛型的基本使用

開發(fā) 后端
泛型,是 Go 語言多年來最令人興奮和根本性的變化之一。沒有泛型,很多人以此「鄙視」Go 語言。當然,也有人覺得根本不需要泛型。有泛型,不代表你一定要用。

[[426677]]

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

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

不過,不少人對泛型還是迷迷糊糊的。本文就嘗試用簡單的術語解釋泛型相關的內容。

01 什么是泛型

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

  1. func Add(a, b intint 

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

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

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

  1. func AddFloat(a, b float64) float64 

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

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

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

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

是不是很簡單?不過看著有點暈?稍微解釋下:

  • Add 后面的 [T any],T 表示類型的標識,any 表示 T 可以是任意類型
  • a、b 和返回值的類型 T 和前面的 T 是同一個類型
  • 為什么用 [],而不是其他語言中的 <>,官方有過解釋,大概就是 <> 會有歧義。曾經(jīng)計劃使用 (),因為太容易混淆,最后使用了 []。

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

  1. Add(1, 2) 
  2. Add(2.1, 3.2) 

不過,這時候代碼會報錯。你可以本地用 Go1.17 啟用泛型的方式試驗,也可以使用 gotip 版本,亦或直接訪問這里試驗:https://go2goplay.golang.org/p/vTHnUA_8vOI

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  
  6. func Add[T any](a, b T) T { 
  7.  return a + b 
  8.  
  9. func main() { 
  10.  fmt.Println(Add(1, 2)) 
  11.  fmt.Println(Add(2.1, 3.2)) 

運行會報錯:

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

為什么?請看下文。

03 約束

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

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

  1. type Addable interface { 
  2.  type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string 

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

首先,Addable 重用了接口語法,即 interface 關鍵字,表示約束,具體約束的類型通過 type 指定,多個用逗號分隔。

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

  1. func Add[T Addable](a, b T "T Addable") T { 
  2.  return a + b 

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

  1. Add("polaris""xu"

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

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

  1. func findFunc[T any](a []T, v T "T any"int { 
  2.  for i, e := range a { 
  3.   if  e == v { 
  4.       return i 
  5.     } 
  6.  } 
  7.  return -1 
  8.  } 

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

  1. package main 
  2.  
  3. func findFunc[T comparable](a []T, v T "T comparable"int { 
  4.  for i, e := range a { 
  5.   if e == v { 
  6.    return i 
  7.   } 
  8.  } 
  9.  return -1 
  10.  
  11. func main() { 
  12.  print(findFunc([]int{1, 2, 3, 4, 5, 6}, 5)) 

04 constraints 包

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

  1. func Max[T any](input []T "T any") (max T) { 
  2.     for _, v := range input { 
  3.         if v > max { 
  4.             max = v 
  5.         } 
  6.     } 
  7.     return 

但運行會報錯:

  1. fmt.Println(Max([]int{1, 4, 2, 10})) 
  2. // cannot compare v > max (operator > not defined for T) 

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

  1. type Ordered interface { 
  2.     type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, string 

因為這樣的需求挺常見的,為了方面,官方提供了一個新包:constraints,預定義了一些約束,具體查看:https://github.com/golang/go/issues/45458。

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

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

05 泛型類型

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

我們知道,Go 支持自定義類型,比如標準庫 sort 包中的 IntSlice:

  1. type IntSlice []int 

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

  1. type Slice[T any] []T 

能看懂吧。

在使用時,針對 int 類型,就是這樣:

  1. x := Slice[int]{1, 2, 3} 

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

  1. func PrintSlice[T any](b Slice[T] "T any"

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

  1. func (b Slice[T]) Print() 

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

當然,泛型類型也可以做類型約束,而不是 any 類型:

  1. type Slice[T comparable] []T 

06 總結

通過本文的講解,相信你對 Go 泛型有了一個基本的掌握。

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

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

本文轉載自微信公眾號「polarisxu」,可以通過以下二維碼關注。轉載本文請聯(lián)系polarisxu公眾號。

 

責任編輯:武曉燕 來源: polarisxu
相關推薦

2024-10-28 00:40:49

Go語法版本

2022-04-15 09:55:59

Go 泛型Go 程序函數(shù)

2023-11-29 08:19:45

Go泛型缺陷

2021-11-27 22:20:13

SlicesGo泛型

2022-03-28 13:34:26

Go泛型部署泛型

2023-11-03 14:02:04

Go切片泛型庫

2021-12-05 23:45:23

Go泛型Maps

2021-12-15 10:23:56

Go 1.18 Bet語言泛型

2022-05-06 09:22:25

Go泛型

2021-10-18 10:53:26

Go 代碼技術

2021-12-01 08:29:17

Go泛型Maps

2022-03-29 11:48:40

Go泛型測試

2021-11-01 12:41:39

Go

2021-12-28 07:20:44

泛型Go場景

2021-12-13 08:52:42

Go 泛型

2021-01-14 05:20:48

Go語言泛型

2021-12-30 18:34:29

緩存GoSinglefligh

2022-07-12 06:17:43

GoogleGolang開發(fā)工作

2022-09-15 14:04:07

Go語言泛型

2025-04-14 08:49:10

點贊
收藏

51CTO技術棧公眾號