Menu
CoddyTech

泛型简介

本课属于 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})。编译器会在编译时检查类型,因此你无需承担运行时开销,就能确保类型安全。

在接下来的课中,我们将详细学习类型参数、约束和泛型结构体。

challenge icon

挑战

简单

让我们使用泛型构建一个分数分析器,同时处理整数和浮点数分数!你将创建一个泛型函数,用于查找切片中的最大值,这样就不必为不同的数值类型编写重复代码了。

你将把代码分成两个文件:

  • 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]" 的格式输出结果,保留一位小数
	}
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器