泛型简介
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 67 课,共 107 课。
在 Go 1.18 之前,为不同类型编写可重用的代码,要么需要重复编写函数,要么使用空接口 any 和类型断言。泛型解决了这个问题,让你可以编写适用于多种类型的函数和类型,同时保留完整的类型安全性。
考虑在切片中查找最小值。没有泛型时,你需要为每种类型分别编写函数:
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}使用泛型时,你可以在方括号中使用类型参数,只编写一次逻辑:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}[T int | float64]声明了一个类型参数T,它可以是int或float64。现在,你可以用同一个函数调用Min([]int{3, 1, 2})或Min([]float64{3.5, 1.2})。编译器会在编译时检查类型,因此你无需承担运行时开销,就能确保类型安全。
在接下来的课中,我们将详细学习类型参数、约束和泛型结构体。
挑战
简单让我们使用泛型构建一个分数分析器,同时处理整数和浮点数分数!你将创建一个泛型函数,用于查找切片中的最大值,这样就不必为不同的数值类型编写重复代码了。
你将把代码分成两个文件:
analyzer.go:定义泛型分析函数。创建一个泛型函数
Max[T int | float64](values []T) T,用于查找并返回切片中的最大值。通过类型参数T,该函数应同时适用于int和float64类型。另外,创建一个泛型函数
Sum[T int | float64](values []T) T,用于计算并返回切片中所有值的总和。main.go:读取输入,并使用不同类型演示你的泛型函数。读取类型标识符(
int或float),然后读取数量,接着读取对应数量的值。根据类型解析这些值,使用相应的类型调用Max和Sum,并打印结果。对于整数输入,打印:
Max (int): [value] Sum (int): [value]对于浮点数输入,打印一位小数:
Max (float): [value] Sum (float): [value]
输入如下:
- 第 1 行:类型标识符(
int或float) - 第 2 行:值的数量(整数)
- 后续各行:每行一个值
例如,给定:
int
4
15
8
23
11你的输出应为:
Max (int): 23
Sum (int): 57再给定:
float
3
4.5
9.2
6.8你的输出应为:
Max (float): 9.2
Sum (float): 20.5注意,同一组泛型函数如何处理整数和浮点数数据:类型参数 [T int | float64] 让编译器能够针对每种用法生成类型安全的代码,而你只需编写一次逻辑。
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取类型标识
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// 读取数量
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// 读取整数值
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: 用整数切片调用 Max 和 Sum
// TODO: 按 "Max (int): [value]" 和 "Sum (int): [value]" 的格式输出结果
} else if typeStr == "float" {
// 读取浮点数值
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: 用 float64 切片调用 Max 和 Sum
// TODO: 按 "Max (float): [value]" 和 "Sum (float): [value]" 的格式输出结果,保留一位小数
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器