柚子快報邀請碼778899分享:Golang 泛型詳解
文章目錄
1. 泛型的類型形參2. 泛型定義4. ~ :指定底層類型3. 類型約束
Go1.18 版本發(fā)布了泛型。
1. 泛型的類型形參
假如我們需要一個計算兩個數(shù)之和的函數(shù),如下定義:
package main
import (
"fmt"
)
// 變量 x, y 為函數(shù)的形參(parameter),int 形參類型
func Sum(x, y int) int {
return x + y
}
func main() {
var a, b int = 2, 6
// 變量 a, b 為調(diào)用函數(shù)的實參(argument)
fmt.Println(Sum(a, b))
}
上例 Sum 函數(shù)僅支持計算 int 類型的數(shù)值,無法計算 int32、float32 等類型的和。
若我們把 int、int32、float32 這些類型作為類型形參代替 int 告訴 Sum 我們所計算的類型是什么,就能滿足我們的需求。這里就用到了泛型的類型形參。
利用泛型改進一下 Sum 函數(shù)
// T:類型形參。調(diào)用 Sum 時,需指定 T 的類型值。
// [T int|int32|float32] 稱為類型形參列表。
// int|int32|float32 為類型約束,表示僅支持列出的類型。
func Sum[T int|int32|float32](x, y T) T{
return x + y
}
func main() {
fmt.Println(Sum[float32](1.1, 2.2)) // 顯示指定參數(shù)的類型
fmt.Println(Sum(1, 2)) // 隱式斷言
}
2. 泛型定義
// 自定義一個新類型 MapT
type MapT[T int|int32|string] map[T]string
上面代碼定義一個新類型 MapT,其中:
T:類型形參。定義 MapT 時 T 代表的類型并不確定,類似一個占位符。int|int32|string:類型約束。指定 T 僅可接受哪些類型。中括號內(nèi) T int|int32|string 定義形參類型,稱為形參類型列表。
其中,類型約束可以包上 interface{}
示例如下:
type Map[T interface{int|int32|string}] map[T]string
func MakeMap[T interface{int|int32|string}](key T, val string) Map[T] {
m := make(Map[T])
m[T] = val
return m
}
// 或簡寫為
type T interface{int|int32|float32|string} // 自定義類型約束
type MapT[t T] map[t]string // 可理解為泛型類型的聲明,后續(xù)聲明都使用 t 代替
// T 為類型約束;必須已定義,否則報錯。
// 返回值 MapT[t] 不可改為 Map[t],因兩者類型約束不同。
func MakeMapT[t T](key t, val string) MapT[t] {
m := make(MapT[])
m[T] = val
return m
}
不同類型使用泛型
type T interface{int|int32|float32|string|bool}
// 泛型類型切片
type SliceT [t T] []t
// 泛型類型結(jié)構(gòu)體
type StructT[t T] struct {
id int
data t
}
// 泛型類型結(jié)構(gòu)體方法
func (s *StructT[t]) SetData(v t) {
s.data = v
}
// 泛型類型 chan
type ChanT[t T] chan t
// 泛型類型接口
type Animal[t T] interface {
Running(t)
}
func main() {
// 下面代碼報錯。SliceT[T] 是泛型類型,不能直接使用 T,必須實例化具體的類型。
var i SliceT[T] = []int{1, 2, 3} // cannot use type T outside a type constraint: interface contains type constraints
// 正確使用
var s SliceT[string] = []string{"Hello", "World"}
fmt.Println(s)
// 結(jié)構(gòu)體方法
s := &StructT[float32]{
id: 1,
}
s.SetData(3.6)
fmt.Println(s)
}
4. ~ :指定底層類型
先看一個示例:
package main
import "fmt"
type T interface { int | int32 | int64 }
type Slice[t T] []t
type MyInt int
func main() {
var s = Slice[int]{1, 2, 3, 4, 5} // ok
fmt.Println(s)
// 泛型類型 Slice[T] 允許 int 作為類型實參;而 MyInt 與 int 屬于兩個類型。
var s2 = Slice[MyInt]{1, 2, 3, 4, 5} // 報錯,MyInt does not satisfy T (possibly missing ~ for int in T)
}
上述示例中,泛型類型 Slice[T] 允許 int 作為類型實參,而不是 MyInt。
雖然 MyInt 底層類型是 int,但它屬于自定義的一個底層為 int 的新類型,而不是 int 類型。
為解決上述問題,Go 新增了一個 ~ 符號;在類型約束中的類型前加 ~ 就表示以該類型為底層類型的類型都可以用于實例化。
把上述代碼中的類型約束 T 優(yōu)化一下就可以正常執(zhí)行。如下:
type T interface { ~int | ~int32 | ~int64 }
使用 ~ 的限制:
~ 后面的類型必須為基本類型。~ 后面類型不能為接口。
3. 類型約束
定義泛型類型時,基礎(chǔ)類型不能只有類型形參 type NewType[T int|float32] T // 報錯, cannot use a type parameter as RHS in type declaration
// 改寫一下
// 編譯沒問題。
// 雖然使用了類型形參,但類型定義未使用形參,底層類型為 int。
type NewType[T int|float32] int
func main() {
var i NewType[int] = 1 // ok,類型形參 int 滿足約束,且賦值滿足底層類型 int
fmt.Println(i) // 1
var f NewType[float32] = 2 // ok,類型形參 float32 滿足約束,且賦值滿足底層類型 int
fmt.Println(f) // 2
var f2 NewType[float32] = 1.2 // 報錯, 類型形參 float32 滿足約束,但實際賦值非 int
// cannot use a type parameter as RHS in type declarationcannot use 1.2
// (untyped float constant) as NewType[float32] value in variable declaration (truncated)
}
約束類型會被編譯器誤認為表達式 type NewType[T *int] []T //報錯, 指針類型會誤認為表達式 *
// 可在約束后加逗號(,)消除歧義
type NewType[T *int|*float32,] []T
// 把約束類型包在 interface{} 內(nèi)(推薦此方法)
type NewType[T interface { *int|*float32 }] []T
約束類型不可重復(fù) type Int interface {int | ~int} // 報錯,int 與 ~int 重復(fù)。
匿名函數(shù)、匿名結(jié)構(gòu)體不支持泛型
柚子快報邀請碼778899分享:Golang 泛型詳解
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。