一:泛型
1.1什么是泛型?
泛型(Generics)是一种编程思想,它允许在编写代码时使用未知的类型。泛型可以增加代码的灵活性和可复用性,同时还能提高代码的安全性和可读性
1.2 Go语言的泛型特点
- 基于类型约束的泛型:Go 泛型通过类型约束来实现泛型,这意味着泛型函数或类型可以接受特定的类型。
- 编译时类型安全:Go 泛型通过编译时类型检查来保证类型安全,这有助于避免运行时错误。
- 支持多种类型:Go 泛型支持多种类型,包括基本类型和自定义类型。
二:语法
在 Golang 中,泛型的语法包括类型参数、类型约束、泛型函数和泛型类型等。
2.1 泛型函数
func FuncName[T Type,...](params) returnType {
// Function body
}
T 表示泛型类型参数,
Type 表示具体的类型,
params 表示函数的参数,
returnType 表示函数的返回值类型。
这样实现一个可以接受任何类型的泛型函数,具体要怎么写呢?
接下来展示一个具体的泛型例子
package main
import "fmt"
func Name[T any](str T) {
fmt.Printf("我是泛型:%v\n", str)
}
func main() {
Name("字符串类型")
Name(123)
}
我是泛型:字符串类型
我是泛型:123
package main
import "fmt"
func Print[T any, U any](x T, y U) {
fmt.Println(x, y)
}
func main() {
Print(1, "haha")
}
2.2 泛型类型
除了泛型函数之外,还有泛型类型
type TypeName[T Type,...] struct {
// Fields
}
TypeName 表示泛型类型名称
T 表示泛型类型参数
Type 表示具体的类型。
举一个具体的例子吧:
package main
type Stack[T any] struct {
data []T
}
func (s *Stack[T]) Push(x T) {
s.data = append(s.data, x)
}
func (s *Stack[T]) Pop() T {
n := len(s.data)
x := s.data[n-1]
s.data = s.data[:n-1]
return x
}
func main() {
s:=Stack[int]{}
s.Push(1)
s.Push(2)
}
在这例子里面就是一个存储int类型的栈,它也可以设置为其他类型的栈
2.3 泛型约束
在使用泛型时,有时需要对泛型类型进行一定的约束。例如,我们希望某个泛型函数或类型只能接受特定类型的参数,或者特定类型的参数必须实现某个接口。在 Go 中,可以使用泛型约束来实现这些需求。
又或者是这些T必须是可以比较的类型,这一点在写泛型链表的时候尤为突出。
2.3.1 接口类型约束
下面是针对其的一个类型约束,实现就是特定的类型才可以,这里以fmt.Stringer接口为例子
它下面有一个string方法,只有实现了这个方法才可以作为这个传入的类型
type Stringer interface {
String() string
}
package main
import (
"fmt"
)
// 自定义类型
type Person struct {
Name string
Age int
}
// 实现fmt.Stringer接口
func (p Person) String() string {
return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
}
// 泛型Print函数
func Print[T fmt.Stringer](x T) {
fmt.Println(x.String())
}
func main() {
p := Person{Name: "Alice", Age: 30}
Print(p) // 输出: Name: Alice, Age: 30
}
package main
import "fmt"
type a interface {
int|float64
}
func Print[T a](x T) {
fmt.Println()
}
func main() {
Print(1)
Print(1.1)
//Print("haha")无法实现这个
}
2.3.2 比较约束
在我们写泛型的时候,如果依旧使用any的话,就会导致一个问题,他表示任意类型,但是如果是任意类型,在使用的时候就会出现一个问题,任意类型要是不可比较怎么办呢?
这就是为什么说要加约束了解决这个问题也很简单,就是把any换为comparabre
如果你使用any,就会出现这个结果
type Node[T comparable] struct {
value T
next *Node[T]
}
2.3.3 组合约束
组合约束就是同时将多个约束组合在一起,也是在接口的基础上实现的
package main
import "fmt"
type a interface {
int | float64
}
type b interface {
comparable
}
type c interface {
a
b
}
func Print[T c](x T) {
fmt.Println()
}
func main() {
Print(1)
}
2.3.4 其他约束
使用golang.org/x/exp/constraints
包中的约束:
这个扩展库提供了一些常见的约束类型,特别是用于数值类型的约束。
例如constraints.Number
、constraints.Integer
、constraints.Float
、constraints.Complex
和constraints.Ordered
等。这些约束在泛型编程中非常有用,特别是当你需要对某些类型的集合(如数值类型)进行操作时。
需要的时候可以学习。
2.4 泛型特化
泛型特化是指将泛型代码转换为具体类型的代码。在 Go 中,泛型特化是在编译期间完成的。特化可以提高代码的性能和运行效率,因为编译器可以针对具体类型进行优化,避免了运行时的类型检查和类型转换。
在 Go 中,泛型特化是通过代码生成器实现的。代码生成器会根据泛型类型或函数的定义,生成具体类型或函数的代码。例如,下面是一个泛型函数的定义:
func Swap[T any](a, b *T) {
*a, *b = *b, *a
}
该函数可以交换任意类型的两个变量的值。在编译期间,代码生成器会根据调用该函数时传递的参数类型生成具体的函数代码。例如,如果传递的是整数类型的指针,代码生成器会生成以下代码:
func Swap_int(a, b *int) {
*a, *b = *b, *a
}
如果传递的是字符串类型的指针,代码生成器会生成以下代码:
func Swap_string(a, b *string) {
*a, *b = *b, *a
}