Golang学习笔记_24——泛型

Golang学习笔记_21——Reader
Golang学习笔记_22——Reader示例
Golang学习笔记_23——error补充



泛型

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()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值