Go 語言泛型使用詳解
1.介紹
Go v1.18 開始支持泛型,距離 Go 當(dāng)前版本 v1.23 已經(jīng)迭代了 5 個大版本了。讀者朋友們在使用 Go 語言開發(fā)時,是否已經(jīng)習(xí)慣使用泛型了呢?
本文我們一起再來回顧學(xué)習(xí)一下 Go 語言中的泛型。
2.概念和語法
概念
Go 語言引入泛型,主要目的是減少代碼重復(fù),提高類型安全。泛型是指在定義函數(shù)(方法)、類型或數(shù)據(jù)結(jié)構(gòu)時,使用類型參數(shù)來表示具體的類型,從而提高代碼的靈活性和可用性。
所謂靈活性,即不需要為每種類型編寫相似代碼。所謂可用性,即在編譯時檢查類型,避免運(yùn)行時錯誤。
語法
所謂類型參數(shù),即類型本身也可以作為一種參數(shù)。在函數(shù)(方法)或類型中,可以使用類型參數(shù)定義通用類型,使用方括號 [] 包含任意合法的標(biāo)識符。
示例代碼:
[T int|string]
閱讀上面這段代碼,T 是類型參數(shù)的形式參數(shù),int、string 是類型參數(shù)的類型約束。
所謂類型約束,實(shí)際上是一個 interface{},通??梢允÷?,但是,如果類型約束特別多時,也可以先定義類型集,再使用,并且可以復(fù)用。
示例代碼:
// 定義
type MyType interface{
int|int8|int32|int64|float32|float64
}
// 使用
[T MyType]
閱讀上面這段代碼,與以往不同, interface{} 中不再是函數(shù)集,而是類型集。也就是說,在 Go v1.18 開始,interface{} 不僅可以定義函數(shù)集,也可以定義類型集。
需要注意的是,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
閱讀上面這段代碼,[] 中包含多個類型參數(shù),需要使用英文逗號 , 分隔,并且類型參數(shù)的形式參數(shù)名字不能相同。
結(jié)構(gòu)體 struct 示例代碼:
type St [T int|string] struct {
id int
name string
salary T
}
需要注意的是,以上所有泛型類型,在使用的時候,需要顯式指定類型的實(shí)參(類型約束),因?yàn)樗恢С诸愋屯茢唷?/p>
以結(jié)構(gòu)體 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
}
// 顯式指定類型參數(shù)的實(shí)際參數(shù)(類型約束),不支持類型推斷
sa := &Salary[int] {
X: 1000,
Y: 2000,
}
value := sa.Min(sa.X, sa.Y)
fmt.Println(value)
需要注意的是,泛型方法的入?yún)⒉恢С肿远x類型參數(shù),示例代碼:
func (s *Salary[T]) Min[T1 int32|float32](x, y T1) T {
if x < y {
return x
}
return y
}
閱讀上面這段代碼,泛型方法的入?yún)?,自定義了類型參數(shù) Min[T1 int32|float32](x, y T1),目前是不支持。
泛型函數(shù)
接下來,我們介紹泛型函數(shù)的使用方式,示例代碼:
func MinNumber[T int|float64](x, y T) T {
if x < y {
return x
}
return y
}
r := MinNumber[int](1, 2)
閱讀上面這段代碼,我們使用類型參數(shù)定義的函數(shù),就是泛型函數(shù)。需要注意的是,在使用函數(shù)時,我們顯式指定函數(shù)入?yún)⒌念愋?r := MinNumber[int](1, 2),實(shí)際上,可以通過類型推斷,通過函數(shù)的入?yún)⑼茢喾盒偷膶?shí)際類型。即 r := MinNumber(1, 2)。
需要注意是,泛型函數(shù)的類型推斷,僅支持函數(shù)的入?yún)?,函?shù)的返回結(jié)果和函數(shù)體是不支持的。
4.總結(jié)
本文我們回顧了 Go v1.18 引入的泛型的語法和使用方式,截止目前,雖然 Go 已經(jīng)迭代了 5 個版本,泛型仍然未得到廣泛使用。
在 Go 未推出泛型之前,Go 社區(qū)的呼聲很大,Go 引入泛型之后,未能得到廣泛使用的原因是一些三方庫和框架,為了追求穩(wěn)定,不愿意大改代碼。
其次,泛型雖然優(yōu)勢明顯,同時也帶來的 Go 語法的復(fù)雜性,這一點(diǎn)有悖于 Go 推崇的使用簡單。
對此我的看法是,建議讀者朋友們積極學(xué)習(xí)和使用泛型,老項(xiàng)目如果不愿意重構(gòu),建議新項(xiàng)目開始使用泛型。