一、泛型
什么是泛型?泛型是類型參數。通俗的說就是給靜態語言一個動態的機會,使用泛型寫出來的是模板代碼,最終的類型會在編譯時確定。
注:泛型的本質是一種模板技術
golang 在最新的 1.18 版本中正式發布了泛型
https://golang.google.cn/doc/go1.18
二、泛型函數
函數泛型只需要在函數名后的"中括號"中申明類型即可,這一點與 scala 語言類似。
與常見的 Java 等語言不同的是 go 的泛型也是有類型的。這種類型是對泛型的一種約束。
這種約束可以來源自基礎類型 comparable, any 也可以來自 interface
注:
- comparable 約束,使得傳入的參數必須能進行 == 與 != 操作
- any 約束,可以是任意類型
- interface 約束其實有三種
1. 如下面例子的可選類型
2. 方法約束(多態)
3. 1 與 2 共同作用
type Number interface {
int | int32 | int64 | float32 | float64
}
func sum[T Number](a, b T) T {
return a * b
}
func main() {
// 調用時指明類型是可選的
// 不寫類型編譯器會自動推導
sum[int](1, 2) // int 類型
sum(2.2, 2.3) // float 類型
...
}
Number 約束了 T 類型只能是 int, int32 int64 float32 float64 中的一個
三、泛型容器
List Map 是兩種最為常見的容器,我們一般會對容器抽象出一些方法,如:
- iterator
- hasElement
- ...
這里使用泛型避免了針對每一個類型的元素都去實現一遍容器的對應方法(邏輯相同,類型不一致...)
注:
- 泛型容器提供了容器的操作模板,里面可以裝任意類型
- golang channel 也是相同的道理
type List[T any] []T
type Map[K comparable, T any] map[K]V
type Chan[T any] chan T
func main() {
// 泛型 List
listInt := List[int]{1, 2, 3}
listFloat := List[float54]{1.2, 2.2, 2.3}
// 泛型 Map
mapStrAny := Map[string, string]{
"1": "cat",
"2": "dog",
}
// 泛型 Chan
cInt := make(Chan[int], 1)
cInt <- 123
cStr := make(Chan[string], 1)
cStr <- "hello world"
...
}
四、泛型結構體
結構體與上述容器一樣也可以定義泛型參數。
Struct 的泛型參數比較特殊的是可以將其帶入到成員方法中...
type Collection[T any] struct {
collect []T
}
func NewCollection[T any](list []T) *Collection[T] {
return &Collection[T]{
collect: list,
}
}
func (s *Collection[T]) Map(trans func(e T) T) *Collection[T] {
var list []T
for _, e := range s.collect {
list = append(list, trans(e))
}
return &Collection[T]{
collect: list,
}
}
func (s *Collection[T]) Result() []T {
return s.collect
}
func main() {
result := NewCollection([]int{1, 2, 3}).Map(func(e int) int { return e * 3 }).Result()
fmt.Println(result)
}