Golang学习笔记_24——泛型

ops/2025/1/16 0:54:45/

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 Tfor _, 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 stringargs 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  TSecond 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_demoimport "fmt"// Generics type constraints
type Number interface {int | int64 | float64 // 使用联合类型表示约束
}func Sum[T Number](nums []T) T {var sum Tfor _, 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  TSecond 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_demoimport ("testing"
)func TestSum(t *testing.T) {type args[T Number] struct {nums []T}type testCase[T Number] struct {name stringargs 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()
}

http://www.ppmy.cn/ops/150426.html

相关文章

Elasticsearch:向量数据库基础设施类别的兴衰

过去几年,我一直在观察嵌入技术如何从大型科技公司的 “秘密武器” 转变为日常开发人员工具。接下来发生的事情 —— 向量数据库淘金热、RAG 炒作周期以及最终的修正 —— 教会了我们关于新技术如何在更广泛的生态系统中找到一席之地的宝贵经验。 更多有关向量搜索…

OpenAI Whisper:语音识别技术的革新者—深入架构与参数

当下语音识别技术正以前所未有的速度发展,极大地推动了人机交互的便利性和效率。OpenAI的Whisper系统无疑是这一领域的佼佼者,它凭借其卓越的性能、广泛的适用性和创新的技术架构,正在重新定义语音转文本技术的规则。今天我们一起了解一下Whi…

安全测评主要标准

大家读完觉得有帮助记得关注和点赞!!! 安全测评的主要标准‌包括多个国际和国内的标准,这些标准为信息系统和产品的安全评估提供了基础和指导。 一、安全测评的主要标准 1.1、国际标准 ‌可信计算机系统评估准则(TC…

linux: 文本编辑器vim

文本编辑器 vi的工作模式 (vim和vi一致) 进入vim的方法 方法一:输入 vim 文件名 此时左下角有 "文件名" 文件行数,字符数量 方法一: 输入 vim 新文件名 此时新建了一个文件并进入vim,左下角有 "文件名"[New File] 灰色的长方形就是光标,输入文字,左下…

代码随想录Day34 | 62.不同路径,63.不同路径II,343.整数拆分,96.不同的二叉搜索树

代码随想录Day34 | 62.不同路径,63.不同路径II,343.整数拆分,96.不同的二叉搜索树 62.不同路径 动态规划第二集: 比较标准简单的一道动态规划,状态转移方程容易想到 难点在于空间复杂度的优化,详见代码 class Solution {public int uniq…

MyBatis-Plus 逆向工程原理及使用指南

概述 MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,它简化了开发人员对数据库的操作,并提供了代码生成器、分页插件等功能。其中的代码生成器(即逆向工程),能够根据数据库中的表结构自动生成实…

【微服务】面试题 6、分布式事务

分布式事务面试题讲解 一、问题背景与解决方案概述 因微服务项目涉及远程调用可能引发分布式事务问题,需解决。主流解决方案有阿里 Seata 框架(含 XA、AT、TCC 模式)和 MQ。 二、Seata 框架关键角色 事务协调者(TC)&…

【Ubuntu与Linux操作系统:九、Shell编程】

第9章 Shell编程 9.1 Shell编程基本步骤 Shell编程是一种通过编写脚本文件,使用Shell解释器执行批处理任务的方法。基本步骤如下: 1. 确定需求 在编写脚本之前,明确要实现的功能,例如文件备份、日志分析或自动化部署等。需求的清…