【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
相关推荐
呼Lu噜34 分钟前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_15838 分钟前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
张帅涛_66640 分钟前
golang goroutine(协程)和 channel(管道) 案例解析
jvm·golang·go
学c真好玩1 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04121 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝1 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel1 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581362 小时前
什么是MCP
后端·程序员
小鸡脚来咯3 小时前
SpringBoot 常用注解通俗解释
java·spring boot·后端