初面网初面网

泛型

泛型是 Go 1.18 引入的特性,让开发者能编写不依赖特定数据类型的可复用代码。核心是两个概念:

概念作用示例
类型参数(Type Parameters)在函数或类型名后声明待定类型[T any]
类型约束(Type Constraints)规定类型参数必须满足的条件int、comparable、any
any任意类型[T any]
comparable可比较的类型(支持 == 和 !=)[K comparable]

为什么需要泛型

传统写法需要为每种类型写重复的函数:

// 处理 int 类型
func MaxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// 处理 float64 类型,几乎一样的代码
func MaxFloat(a, b float64) float64 {
	if a > b {
		return a
	}
	return b
}

使用泛型后,一个函数处理多种类型:

// 一个函数处理多种类型
func Max[T comparable](a, b T) T {
	if a > b {
		return a
	}
	return b
}

泛型语法

泛型函数和类型通过类型参数列表声明,语法为 [类型参数 约束]

func 函数名[T 约束](参数 T) 返回值类型 {
	// 函数体
}

type 类型名[T 约束] struct {
	// 结构体字段
}

类型参数命名约定:通常用大写字母,T 表示 Type,K 表示 Key,V 表示 Value,E 表示 Element。

内置约束

any 约束

any 是空接口 interface{} 的别名,表示任何类型都可以。

package main

import "fmt"

func PrintAny[T any](value T) {
	fmt.Printf("Value: %v, Type: %T\n", value, value)
}

func main() {
	PrintAny(42)       // Value: 42, Type: int
	PrintAny("hello")  // Value: hello, Type: string
	PrintAny(3.14)     // Value: 3.14, Type: float64
}

comparable 约束

表示类型支持 == 和 != 操作符,可用来做相等判断。

package main

import "fmt"

func FindIndex[T comparable](slice []T, target T) int {
	for i, v := range slice {
		if v == target {
			return i
		}
	}
	return -1
}

func main() {
	numbers := []int{1, 2, 3, 4, 5}
	fmt.Println(FindIndex(numbers, 3))   // 2

	names := []string{"Alice", "Bob", "Charlie"}
	fmt.Println(FindIndex(names, "Bob")) // 1
}

联合约束

使用 | 运算符把多个类型组合成一个约束。

package main

import "fmt"

// 数字类型约束
type Number interface {
	int | int8 | int16 | int32 | int64 |
		uint | uint8 | uint16 | uint32 | uint64 |
		float32 | float64
}

func Add[T Number](a, b T) T {
	return a + b
}

func main() {
	fmt.Println(Add(10, 20))     // 30
	fmt.Println(Add(3.14, 2.71)) // 5.85
}

自定义约束:方法约束

定义需要特定方法的约束,约束类型必须实现指定方法。

package main

import "fmt"

type Stringer interface {
	String() string
}

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func PrintString[T Stringer](value T) {
	fmt.Println(value.String())
}

func main() {
	person := Person{Name: "Alice", Age: 25}
	PrintString(person) // Alice (25 years old)
}

泛型函数示例

package main

import "fmt"

// 交换两个值
func Swap[T any](a, b T) (T, T) {
	return b, a
}

// 判断切片是否包含元素
func Contains[T comparable](slice []T, target T) bool {
	for _, item := range slice {
		if item == target {
			return true
		}
	}
	return false
}

func main() {
	a, b := Swap(10, 20)
	fmt.Println(a, b) // 20 10

	numbers := []int{1, 2, 3, 4, 5}
	fmt.Println(Contains(numbers, 3)) // true
}

泛型在编译时进行类型特化,运行性能与手写特定类型代码相当

泛型结构体示例

package main

import "fmt"

// 泛型栈实现
type Stack[T any] struct {
	elements []T
}

// 入栈
func (s *Stack[T]) Push(value T) {
	s.elements = append(s.elements, value)
}

// 出栈
func (s *Stack[T]) Pop() (T, bool) {
	if len(s.elements) == 0 {
		var zero T
		return zero, false
	}
	lastIndex := len(s.elements) - 1
	value := s.elements[lastIndex]
	s.elements = s.elements[:lastIndex]
	return value, true
}

func main() {
	// 整数栈
	intStack := Stack[int]{}
	intStack.Push(1)
	intStack.Push(2)
	intStack.Push(3)
	fmt.Println(intStack.Pop()) // 3 true

	// 字符串栈
	stringStack := Stack[string]{}
	stringStack.Push("hello")
	stringStack.Push("world")
	fmt.Println(stringStack.Pop()) // world true
}

类型推断

Go 编译器能够自动推断类型参数,无需显式指定。

func main() {
	// 编译器自动推断 T 为 int
	fmt.Println(Max([]int{1, 2, 3}))

	// 显式指定类型(有时需要)
	var result int = Max[int]([]int{1, 2, 3})
	fmt.Println(result)
}

选择约束时:any 最灵活但功能受限;comparable 支持相等比较;联合约束可限制具体类型范围

更新于 2026/8/13