Go 語言泛型使用詳解
1.介紹
Go v1.18 開始支持泛型,距離 Go 當前版本 v1.23 已經迭代了 5 個大版本了。讀者朋友們在使用 Go 語言開發時,是否已經習慣使用泛型了呢?
本文我們一起再來回顧學習一下 Go 語言中的泛型。
2.概念和語法
概念
Go 語言引入泛型,主要目的是減少代碼重復,提高類型安全。泛型是指在定義函數(方法)、類型或數據結構時,使用類型參數來表示具體的類型,從而提高代碼的靈活性和可用性。
所謂靈活性,即不需要為每種類型編寫相似代碼。所謂可用性,即在編譯時檢查類型,避免運行時錯誤。
語法
所謂類型參數,即類型本身也可以作為一種參數。在函數(方法)或類型中,可以使用類型參數定義通用類型,使用方括號 [] 包含任意合法的標識符。
示例代碼:
[T int|string]
閱讀上面這段代碼,T 是類型參數的形式參數,int、string 是類型參數的類型約束。
所謂類型約束,實際上是一個 interface{},通常可以省略,但是,如果類型約束特別多時,也可以先定義類型集,再使用,并且可以復用。
示例代碼:
// 定義
type MyType interface{
int|int8|int32|int64|float32|float64
}
// 使用
[T MyType]
閱讀上面這段代碼,與以往不同, interface{} 中不再是函數集,而是類型集。也就是說,在 Go v1.18 開始,interface{} 不僅可以定義函數集,也可以定義類型集。
需要注意的是,Go v1.18 開始,interface{} 不僅可以包含任意類型,還可以包含任意類型集,或共享相同底層類型的所有類型。
示例代碼:
type A interface{
int|float64
}
type B interface{
string|bool
}
type C interface{
A|B
}
type MyString ~string
閱讀上面這段代碼,~string 代表 string 類型本身,和以 string 類型為底層類型的所有類型。
3.使用方式
在了解完泛型的概念和語法之后,接下來,我們介紹泛型的使用方式。
泛型類型
切片 slice 示例代碼:
type Sl [T int|float64] []T
映射 map 示例代碼:
type M [K string|int, V string|int] map[K]V
閱讀上面這段代碼,[] 中包含多個類型參數,需要使用英文逗號 , 分隔,并且類型參數的形式參數名字不能相同。
結構體 struct 示例代碼:
type St [T int|string] struct {
id int
name string
salary T
}
需要注意的是,以上所有泛型類型,在使用的時候,需要顯式指定類型的實參(類型約束),因為它不支持類型推斷。
以結構體 struct 為例,示例代碼:
coder := &St[int]{
id: 1,
name: "frank",
salary: 1000,
}
fmt.Printf("%+v\n", coder)
泛型方法
接下來,我們介紹泛型類型的泛型方法,示例代碼:
type Salary[T int|float64] struct {
X,Y T
}
func (s *Salary[T]) Min(x, y T) T {
if x < y {
return x
}
return y
}
// 顯式指定類型參數的實際參數(類型約束),不支持類型推斷
sa := &Salary[int] {
X: 1000,
Y: 2000,
}
value := sa.Min(sa.X, sa.Y)
fmt.Println(value)
需要注意的是,泛型方法的入參不支持自定義類型參數,示例代碼:
func (s *Salary[T]) Min[T1 int32|float32](x, y T1) T {
if x < y {
return x
}
return y
}
閱讀上面這段代碼,泛型方法的入參,自定義了類型參數 Min[T1 int32|float32](x, y T1),目前是不支持。
泛型函數
接下來,我們介紹泛型函數的使用方式,示例代碼:
func MinNumber[T int|float64](x, y T) T {
if x < y {
return x
}
return y
}
r := MinNumber[int](1, 2)
閱讀上面這段代碼,我們使用類型參數定義的函數,就是泛型函數。需要注意的是,在使用函數時,我們顯式指定函數入參的類型 r := MinNumber[int](1, 2),實際上,可以通過類型推斷,通過函數的入參推斷泛型的實際類型。即 r := MinNumber(1, 2)。
需要注意是,泛型函數的類型推斷,僅支持函數的入參,函數的返回結果和函數體是不支持的。
4.總結
本文我們回顧了 Go v1.18 引入的泛型的語法和使用方式,截止目前,雖然 Go 已經迭代了 5 個版本,泛型仍然未得到廣泛使用。
在 Go 未推出泛型之前,Go 社區的呼聲很大,Go 引入泛型之后,未能得到廣泛使用的原因是一些三方庫和框架,為了追求穩定,不愿意大改代碼。
其次,泛型雖然優勢明顯,同時也帶來的 Go 語法的復雜性,這一點有悖于 Go 推崇的使用簡單。
對此我的看法是,建議讀者朋友們積極學習和使用泛型,老項目如果不愿意重構,建議新項目開始使用泛型。