Go 语言泛型

Go 1.18 正式引入泛型(Generics),也叫类型参数。此前想写一个"取切片第一个元素"的函数,int、string、float64 每种类型都得复制一份几乎相同的代码;泛型允许把类型写成参数,调用时才确定,一份代码通吃所有类型。(说明:片段需放入 main 函数运行)

泛型解决什么问题

func firstInt(s []int) int { return s[0] } // 泛型之前:逐类型复制
func first[T any](s []T) T { return s[0] } // 泛型之后:一份通吃

泛型函数语法

在函数名后加方括号声明类型参数:[T any] 表示 T 可以是任意类型,参数与返回值里都可使用 T;同一函数被不同具体类型实例化时,编译器自动生成对应版本。

package main

import "fmt"

func first[T any](s []T) T { // any 即 interface{} 的别名
	return s[0]
}

func main() {
	fmt.Println(first([]int{1, 2, 3}))      // 输出:1
	fmt.Println(first([]string{"a", "b"}))  // 输出:a
	fmt.Println(first([]float64{9.9, 8.8})) // 输出:9.9
}

类型约束:any、comparable 与自定义

约束决定 T 能做什么运算:any 最宽,只能传参、返回;想比较相等要用内置约束 comparable

func Index[T comparable](list []T, want T) int {
	for i, v := range list {
		if v == want {
			return i
		}
	}
	return -1
}
// 放入 main 调用:
// fmt.Println(Index([]string{"春", "夏"}, "夏")) // 输出:1

想做加法、取大小,得用自定义约束(Go 1.18 起接口可以写类型集合):

type Number interface {
	int | int64 | float64 // 只允许这三种类型
}

func Sum[T Number](nums []T) T {
	var total T
	for _, v := range nums {
		total += v // 有约束才能用 + 运算
	}
	return total
}
// fmt.Println(Sum([]int{1, 2, 3}))      // 输出:6
// fmt.Println(Sum([]float64{1.5, 2.5})) // 输出:4

一句话:若自定义类型 type MyInt int 也想匹配 int,约束写成 ~int,表示"底层类型为 int 的所有类型"。

泛型类型:泛型栈示例

结构体等类型定义也能带类型参数,比如支持任意元素类型的栈:

type Stack[T any] struct {
	items []T
}

func (s *Stack[T]) Push(v T) { // 方法上也要带 [T]
	s.items = append(s.items, v)
}

func (s *Stack[T]) Pop() (T, bool) {
	if len(s.items) == 0 {
		var zero T // 用零值返回
		return zero, false
	}
	v := s.items[len(s.items)-1]
	s.items = s.items[:len(s.items)-1]
	return v, true
}
// 放入 main:
// st := Stack[int]{}
// st.Push(10)
// v, ok := st.Pop()
// fmt.Println(v, ok) // 输出:10 true

小结:Go 1.18 引入泛型,用 [T 约束] 消灭重复的类型代码;any 最宽、comparable 可比较,自定义约束用类型集合,~ 表示底层类型。

笔记加载中…