Golang学习笔记_24——泛型
Golang学习笔记_21——Reader
Golang学习笔记_22——Reader示例
Golang学习笔记_23——error补充
文章目录
- 泛型
- 1. 泛型中的类型参数
- 1.1 类型参数声明
- 1.2 类型参数的约束
- 1.3 类型参数的实例化
- 2. 泛型函数
- 3. 泛型类型
- 4. 泛型接口
- 源码
泛型
Go语言从1.18版本开始引入了泛型,这是Go语言的一个重要特性,它允许函数、类型和接口在定义时不必绑定到特定的类型上,而是可以在后续使用时再指定具体的类型。这极大地增强了代码的复用性和灵活性。
1. 泛型中的类型参数
类型参数是泛型函数、泛型类型和泛型接口定义中声明的占位符,它们用于表示在泛型实例化时可以指定的具体类型。
1.1 类型参数声明
在泛型定义中,类型参数通过方括号[]中的类型参数列表进行声明。
func Print[T any](x T) {...}
type Pair[T, U any] struct {...}
type Describer[T any] interface {...}
1.2 类型参数的约束
类型参数可以受到约束,这意味着它们必须满足特定的接口。这通过类型约束来实现。
// Generics type constraints
type Number interface {
int | int64 | float64 // 使用联合类型表示约束
}
func Sum[T Number](nums []T) T {
var sum T
for _, num := range nums {
sum += num
}
return sum
}
测试方法
func TestSum(t *testing.T) {
type args[T Number] struct {
nums []T
}
type testCase[T Number] struct {
name string
args args[T]
want T
}
intTests := []testCase[int]{
{
name: "int add",
args: args[int]{
nums: []int{1, 2, 3},
},
want: 6,
},
}
floatTests := []testCase[float64]{
{
name: "float add",
args: args[float64]{
nums: []float64{1.1, 2.2, 3.3},
},
want: 6.6,
},
}
strTests := []testCase[string]{
{
name: "string add",
args: args[string]{
nums: []string{"1", "2", "3"},
},
want: "123",
},
}
for _, tt := range strTests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.nums); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
for _, tt := range intTests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.nums); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
for _, tt := range floatTests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.nums); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
}
输出结果
strTests 测试结果
# Golang/generics_demo [Golang/generics_demo.test]
./generics_demo_test.go:37:25: string does not satisfy Number (string missing in int | int64 | float64)
./generics_demo_test.go:40:15: string does not satisfy Number (string missing in int | int64 | float64)
./generics_demo_test.go:48:17: string does not satisfy Number (string missing in int | int64 | float64)
intTests floatTests 测试结果
=== RUN TestSum
=== RUN TestSum/int_add
=== RUN TestSum/float_add
--- PASS: TestSum (0.00s)
--- PASS: TestSum/int_add (0.00s)
--- PASS: TestSum/float_add (0.00s)
PASS
1.3 类型参数的实例化
在调用泛型函数、创建泛型类型的实例或赋值给泛型接口时,类型参数会被具体化(实例化)。这可以显式地通过类型参数列表进行,也可以由编译器自动推断。
Print[int](42) // 显式指定T为int类型
Print(3.14) // 编译器自动推断T为float64类型
var p Pair[int, string] // 显式指定T为int,U为string类型
2. 泛型函数
泛型函数允许在函数签名中声明类型参数,这些参数将在函数调用时具体化。
func genericFuncDemo[T any](value T) {
fmt.Println(value)
}
func testGenericFuncDemo() {
genericFuncDemo(1)
genericFuncDemo("hello")
genericFuncDemo(1.1)
}
测试方法
=== RUN Test_testGenericFuncDemo
1
hello
1.1
--- PASS: Test_testGenericFuncDemo (0.00s)
PASS
3. 泛型类型
type Pair[T, U any] struct {
First T
Second U
}
func PairDemo() {
pair := Pair[int, string]{First: 10, Second: "hello"}
fmt.Println(pair.First, pair.Second)
}
测试方法
func TestPairDemo(t *testing.T) {
PairDemo()
}
输出结果
=== RUN TestPairDemo
10 hello
--- PASS: TestPairDemo (0.00s)
PASS
4. 泛型接口
// generics interface
type Describer[T any] interface {
Describe(T) string
}
type IntDescriber struct {
}
func (num IntDescriber) Describe(i int) string {
return fmt.Sprintf("类型是:%d", i)
}
func DescribeDemo() {
var test Describer[int]
test = IntDescriber{}
fmt.Println(test.Describe(100))
}
测试方法
func TestDescribeDemo(t *testing.T) {
DescribeDemo()
}
输出结果
=== RUN TestDescribeDemo
类型是:100
--- PASS: TestDescribeDemo (0.00s)
PASS
源码
// generics_demo.go 文件
package generics_demo
import "fmt"
// Generics type constraints
type Number interface {
int | int64 | float64 // 使用联合类型表示约束
}
func Sum[T Number](nums []T) T {
var sum T
for _, num := range nums {
sum += num
}
return sum
}
func genericFuncDemo[T any](value T) {
fmt.Println(value)
}
func testGenericFuncDemo() {
genericFuncDemo(1)
genericFuncDemo("hello")
genericFuncDemo(1.1)
}
type Pair[T, U any] struct {
First T
Second U
}
func PairDemo() {
pair := Pair[int, string]{First: 10, Second: "hello"}
fmt.Println(pair.First, pair.Second)
}
// generics interface
type Describer[T any] interface {
Describe(T) string
}
type IntDescriber struct {
}
func (num IntDescriber) Describe(i int) string {
return fmt.Sprintf("类型是:%d", i)
}
func DescribeDemo() {
var test Describer[int]
test = IntDescriber{}
fmt.Println(test.Describe(100))
}
// generics_demo_test.go 文件
package generics_demo
import (
"testing"
)
func TestSum(t *testing.T) {
type args[T Number] struct {
nums []T
}
type testCase[T Number] struct {
name string
args args[T]
want T
}
intTests := []testCase[int]{
{
name: "int add",
args: args[int]{
nums: []int{1, 2, 3},
},
want: 6,
},
}
floatTests := []testCase[float64]{
{
name: "float add",
args: args[float64]{
nums: []float64{1.1, 2.2, 3.3},
},
want: 6.6,
},
}
//strTests := []testCase[string]{
// {
// name: "string add",
// args: args[string]{
// nums: []string{"1", "2", "3"},
// },
// want: "123",
// },
//}
//for _, tt := range strTests {
// t.Run(tt.name, func(t *testing.T) {
// if got := Sum(tt.args.nums); got != tt.want {
// t.Errorf("Sum() = %v, want %v", got, tt.want)
// }
// })
//}
for _, tt := range intTests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.nums); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
for _, tt := range floatTests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.nums); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
}
func Test_testGenericFuncDemo(t *testing.T) {
testGenericFuncDemo()
}
func TestPairDemo(t *testing.T) {
PairDemo()
}
func TestDescribeDemo(t *testing.T) {
DescribeDemo()
}