【Go语言成长之路】泛型入门

文章目录

泛型入门

​ 本教程介绍了Go中泛型(generics)的基础知识,并且将声明两个简单的非泛型函数,然后在单个泛型函数中捕获相同的逻辑。

一、前提

  • Go1.18以及更高的版本

二、创建项目

创建一个名为 generics 的目录:

sh 复制代码
~$ mkdir generics
~$ cd generics

创建模块:

sh 复制代码
~/generics$ go mod init example/generics
go: creating new go.mod: module example/generics

三、调用非泛型函数

在此步骤中,将会添加两个函数,每个函数将map的值相加并返回总计。

注:这里需要声明两种不同类型的maps: 一个用于存储int64的值,一个用于存储float64的值。

接下来就是编写代码:

go 复制代码
package main

import "fmt"

// SumInts takes a map of string to int64 values.
func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}

	return s
}

// SumFloats takes a map of string to float64 values.
func SumFloats(m map[string]float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first":  34,
		"second": 12,
	}
	// Initialize a map for the float values
	floats := map[string]float64{
		"first":  35.98,
		"second": 26.99,
	}
	// Call the two functions you declared earlier to find the sum of each map's values
	// and print the result
	fmt.Printf("Non-Generic Sums: %v and %v\n",
		SumInts(ints),
		SumFloats(floats))
}

运行代码:

sh 复制代码
~/generics$ go run .
Non-Generic Sums: 46 and 62.97

四、调用泛型函数处理多种类型

​ 在此步骤中,将通过编写一个泛型函数,该函数可以接受包含int或者float的map。

​ 要想实现该功能,有以下几点需要考虑:

  1. 支持任意类型: 需要一种方法来声明支持的类型
  2. 判断类型: 判断它是使用int或者flaot的map。
  3. 处理类型: 除了普通的函数参数依赖还需要有type参数,使得能够处理不同类型的参数。可以通过type参数和普通参数一起来调用函数。
  4. type类型约束: 每个type参数都有一个type约束,指定调用代码可用的type参数。如果type参数的约束不允许指定的type参数,则代码将无法编译。

注: type参数必须支持泛型代码对其执行的所有操作。

接下来就是编写代码:

go 复制代码
package main

import "fmt"

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
// Declare a SumIntsOrFloats function with two type parameters (inside the square brackets), K and V
// and one argument that uses the type parameters, m of type map[K]V. The function returns a value of type V.
// Specify for the K type parameter the type constraint comparable.the comparable constraint is predeclared in Go. It allows any type whose values may be used as an operand of the comparison operators == and !=.
// Specify for the V type parameter a constraint that is a union of two types: int64 and float64. Using | specifies a union of the two types, meaning that this constraint allows either type.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first":  34,
		"second": 12,
	}
	// Initialize a map for the float values
	floats := map[string]float64{
		"first":  35.98,
		"second": 26.99,
	}
	fmt.Printf("Generic Sums: %v and %v\n",
		SumIntsOrFloats[string, int64](ints),
		SumIntsOrFloats[string, float64](floats))
}

运行代码:

sh 复制代码
~/generics$ go run .
Generic Sums: 46 and 62.97

五、不使用类型参数调用泛型函数

当 Go 编译器可以推断出您要使用的类型时,可以在调用代码中省略类型参数。编译器从函数参数的类型推断类型参数。

请: 如果需要调用没有参数的泛型函数,则需要在函数调用中包含类型参数。

修改上面的代码中的打印部分代码:

go 复制代码
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
    SumIntsOrFloats(ints),
    SumIntsOrFloats(floats))

结果和上面一致。

六、声明类型约束为接口

将类型约束声明为一个接口。

go 复制代码
// Declare the Number interface type to use as a type constraint.
type Number interface {
    // Declare a union of int64 and float64 inside the interface.
    int64 | float64
}

这样,当想要将类型参数约束为 int64float64 时,可以使用此 Number 类型约束,而不是写出 int64 |float64

之后在该接口粘贴如下代码:

go 复制代码
// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

main.go中,可以通过如下方式来调用:

go 复制代码
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
    SumNumbers(ints),
    SumNumbers(floats))

运行代码:

sh 复制代码
~/generics$ go run .
Generic Sums with Constraint: 46 and 62.97
相关推荐
机器之心2 小时前
图学习新突破:一个统一框架连接空域和频域
人工智能·后端
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
顽疲3 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
机器之心3 小时前
AAAI 2025|时间序列演进也是种扩散过程?基于移动自回归的时序扩散预测模型
人工智能·后端
hanglove_lucky4 小时前
本地摄像头视频流在html中打开
前端·后端·html
皓木.5 小时前
(自用)配置文件优先级、SpringBoot原理、Maven私服
java·spring boot·后端
i7i8i9com5 小时前
java 1.8+springboot文件上传+vue3+ts+antdv
java·spring boot·后端
秋意钟5 小时前
Spring框架处理时间类型格式
java·后端·spring
我叫啥都行6 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
Stark、6 小时前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端