从1.18版本开始,Go添加了对泛型的支持,即类型参数
一、泛型函数
如果我们要实现一个对int类型的求和函数
func add(a, b int) int {
return a + b
}
但是这样写了之后,如果参数是float类型,int32类型这些,就没办法使用了。使用泛型进行问题解决
func add[T int | float64 | int32](a, b T) T {
return a + b
}
二、泛型结构体
以下是一个返回泛型结构体案例
package main
import (
"encoding/json"
"fmt"
)
type Response[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data T `json:"data"`
}
type User struct {
Name string `json:"name"`
}
type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var userResponse Response[User]
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"大冬"}}`), &userResponse)
fmt.Println(userResponse.Data.Name) // 大冬
var userInfoResponse Response[UserInfo]
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"大冬","age":24}}`), &userInfoResponse)
fmt.Println(userInfoResponse.Data.Name, userInfoResponse.Data.Age) // 大冬 24
}
三、泛型切片
泛型切片可以用于存储不同类型的元素。以下是一个示例:
package main
import "fmt"
func storeElements[T any](elements []T) {
for _, element := range elements {
// 可以对元素进行操作
fmt.Println(element)
}
}
func main() {
intElements := []int{1, 2, 3}
floatElements := []float64{4.5, 6.7, 8.9}
stringElements := []string{"hello", "world"}
storeElements(intElements)
storeElements(floatElements)
storeElements(stringElements)
}
四、泛型Map
泛型 Map 可以用于存储具有不同键值类型的映射。以下是一个示例:
package main
import "fmt"
// 定义一个泛型 Map
type GenericMap[K any, V any] map[K]V
func main() {
// 创建一个 int 到 string 的泛型 Map
intToStringMap := GenericMap[int, string]{
1: "one",
2: "two",
3: "three",
}
// 访问和操作泛型 Map
fmt.Println(intToStringMap[1]) // one
intToStringMap[4] = "four"
fmt.Println(intToStringMap) // map[1:one 2:two 3:three 4:four]
}
五、泛型联合类型
5.1 类型约束接口
type Integer int
type Number interface {
int | int64 | float64 | float32
}
// 支持衍生类型的语法
type Number interface {
~int | int64 | float64 | float32
}
这是一个 类型联合(union)接口,表示
T只能是这四种数值类型之一。这是 Go 泛型中 自定义约束 的一种方式。
~int:代表int以及int的衍生类型也可以传入
5.2 泛型函数
func Sum[T Number](vals ...T) T {
var res T
for _, val := range vals {
res = res + val
}
return res
}
[T Number]是 类型参数列表,表示T必须满足Number接口。vals ...T是 变长参数,类型为T。var res T是 零值初始化,对于数值类型就是0或0.0。
5.3 使用示例
package main
import "fmt"
func main() {
fmt.Println(Sum(1, 2, 3)) // 6 (int)
fmt.Println(Sum(1.1, 2.2, 3.3)) // 6.6 (float64)
fmt.Println(Sum[int64](10, 20)) // 30 (显式指定类型)
}
六、可比较类型
把「需要用到 == 运算符」的类型参数写成
[T comparable]
package main
import "fmt"
// SliceContains 判断切片里是否包含给定元素
// 约束:T 必须支持 == 比较,因此用 comparable
func SliceContains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target { // 这里用了 ==,只有 comparable 才合法
return true
}
}
return false
}
func main() {
// 基本类型
fmt.Println(SliceContains([]int{1, 2, 3}, 2)) // true
// 字符串
fmt.Println(SliceContains([]string{"go", "rust"}, "python")) // false
// 结构体(只要字段全都 comparable 即可)
type Point struct{ X, Y int }
ps := []Point{{1, 2}, {3, 4}}
fmt.Println(SliceContains(ps, Point{3, 4})) // true
}