golang泛型Generics的實(shí)現(xiàn)
Go 泛型(Generics)自 Go 1.18(2022年3月)引入,允許編寫適用于多種類型的通用代碼,避免重復(fù)實(shí)現(xiàn)。以下是核心概念和使用指南:
1. 基本語法
泛型通過 類型參數(shù)(Type Parameters) 實(shí)現(xiàn),使用方括號(hào) [] 聲明:
// 泛型函數(shù):求最大值
func Max[T constraints.Ordered](slice []T) T {
if len(slice) == 0 {
var zero T
return zero
}
max := slice[0]
for _, v := range slice {
if v > max {
max = v
}
}
return max
}
// 使用
ints := []int{1, 3, 2, 5, 4}
floats := []float64{1.1, 3.3, 2.2}
strings := []string{"apple", "banana"}
fmt.Println(Max(ints)) // 5
fmt.Println(Max(floats)) // 3.3
fmt.Println(Max(strings)) // "banana"2. 類型約束(Constraints)
約束限制類型參數(shù)的范圍,常用約束來自 golang.org/x/exp/constraints 或標(biāo)準(zhǔn)庫:
| 約束 | 含義 | 適用類型 |
|---|---|---|
| any | 任意類型 | 所有類型 |
| comparable | 可比較(==、!=) | 支持比較的類型 |
| constraints.Ordered | 有序(可 <、> 比較) | int, float, string 等 |
| interface{ Method() } | 必須實(shí)現(xiàn)指定方法 | 滿足接口的類型 |
// 自定義約束:必須支持 String() 方法
type Stringer interface {
String() string
}
func PrintAll[T Stringer](items []T) {
for _, item := range items {
fmt.Println(item.String())
}
}3. 泛型數(shù)據(jù)結(jié)構(gòu)
泛型最常見的用途是實(shí)現(xiàn)通用數(shù)據(jù)結(jié)構(gòu),避免為每種類型重復(fù)編寫代碼:
// 泛型棧
type Stack[T any] struct {
data []T
}
func (s *Stack[T]) Push(item T) {
s.data = append(s.data, item)
}
func (s *Stack[T]) Pop() (T, error) {
var zero T
if len(s.data) == 0 {
return zero, errors.New("stack is empty")
}
item := s.data[len(s.data)-1]
s.data = s.data[:len(s.data)-1]
return item, nil
}
// 使用
intStack := Stack[int]{}
strStack := Stack[string]{}4. 何時(shí)使用泛型?(官方建議)
Go 泛型設(shè)計(jì)者 Ian Lance Taylor 給出的指導(dǎo)原則:
? 適合使用泛型
- 通用數(shù)據(jù)結(jié)構(gòu):鏈表、樹、棧等(代碼與元素類型無關(guān))
- 處理內(nèi)置容器:對(duì) slice、map、channel 進(jìn)行通用操作(如提取 map 的所有 key)
- 不同類型實(shí)現(xiàn)相同邏輯:如
Len()、Swap()等方法實(shí)現(xiàn)完全一致
// 提取 map 的所有 key(與 value 類型無關(guān))
func MapKeys[Key comparable, Val any](m map[Key]Val) []Key {
s := make([]Key, 0, len(m))
for k := range m {
s = append(s, k)
}
return s
}? 不適合使用泛型
- 僅調(diào)用方法時(shí):直接用
interface(如io.Reader),不要寫成func Read[T io.Reader](r T) - 不同實(shí)現(xiàn)邏輯時(shí):用接口+多態(tài),而非泛型(如文件讀取 vs 隨機(jī)數(shù)生成器的
Read) - 為了性能優(yōu)化:泛型實(shí)例化后的代碼通常不會(huì)比 interface 更快
5. 重要陷阱與最佳實(shí)踐
避免指針類型作為類型參數(shù)
// ? 錯(cuò)誤:T 是類型參數(shù),不是指針,無法解引用
func Set[T *int|*uint](ptr T) { *ptr = 1 }
// ? 正確:明確使用 *T
func Set[T int|uint](ptr *T) { *ptr = 1 }核心原則
- 從寫函數(shù)開始,發(fā)現(xiàn)重復(fù)代碼時(shí)再引入類型參數(shù),不要先定義約束
- 優(yōu)先用函數(shù)而非方法:傳入 func(T, T) bool 比較函數(shù),比要求類型實(shí)現(xiàn) Compare() 方法更靈活
- 明確使用 *T、[]T、map[K]V,不要讓 T 代表指針、slice 或 map 本身
- 不要過度泛型化:如果接口(interface)能解決問題,就不要用泛型
6. 類型集合(Type Sets)
Go 1.18+ 支持使用 | 定義類型集合:
// 只接受 int 或 string
func Process[T int | string](val T) {
fmt.Println(val)
}
// 結(jié)合接口約束
type Number interface {
~int | ~int64 | ~float64 // ~ 表示底層類型
}
func Sum[T Number](vals []T) T {
var sum T
for _, v := range vals {
sum += v
}
return sum
}~int 表示底層類型為 int 的所有類型(包括 type MyInt int 這種自定義類型)。
總結(jié):Go 泛型的設(shè)計(jì)哲學(xué)是 "用代碼寫程序,而不是用類型定義寫程序"。當(dāng)你發(fā)現(xiàn)自己在復(fù)制粘貼幾乎相同的代碼、只改類型時(shí),就是引入泛型的最佳時(shí)機(jī)。但 Go 的 interface 機(jī)制已經(jīng)非常強(qiáng)大,如果只是調(diào)用方法,優(yōu)先使用 interface 而非泛型。
到此這篇關(guān)于golang泛型Generics的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)golang泛型Generics內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang使用Apache PLC4X連接modbus的示例代碼
Modbus是一種串行通信協(xié)議,是Modicon公司于1979年為使用可編程邏輯控制器(PLC)通信而發(fā)表,這篇文章主要介紹了Golang使用Apache PLC4X連接modbus的示例代碼,需要的朋友可以參考下2024-07-07
gorm update傳入struct對(duì)象,零值字段不更新的解決方案
這篇文章主要介紹了gorm update傳入struct對(duì)象,零值字段不更新的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04

