Golang 泛型学习

Golang 泛型

今天来学习下Golang中泛型的基础知识。使用泛型,开发者可以声明一个函数来适应不同类型的参数,避免由于参数类型不一致而声明多个处理逻辑类似的函数。在本教程中,将声明两个简单的非泛型函数,然后在单个泛型函数中实现相同的逻辑。

基本要求

  • Go 1.18 及更高版本
  • 合适的编译工具 - text编辑器也满足要求
  • 命令终端 - Linux、Mac系统shell, Windows系统的Cmd、PowerShell

创建目录

新建代码目录

打开shell终端,使用以下命令创建文件夹,作为后续代码案例的根目录

$ mkdir generics
$ cd generics

初始化项目

使用go 初始化命令,对项目进行初始化

$ go mod init example/generics

在这里插入图片描述

项目初始化之后,就可以编写一些简单的示例代码,为了方便对比非泛型函数与泛型函数之间的区别,我们先学习一下非泛型函数使用过程中的缺陷。

非泛型函数

在这个章节中,增加两个函数,每个函数将参数值进行累加并返回。在以下的示例中,由于参数类型的不同(一个是int64类型参数,一个是float64类型参数),开发者必须声明两个函数满足业务的要求(即时函数内部的逻辑处理都一致)

逻辑代码

新增main.go代码文件,代码内容如下

package main

import "fmt"

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
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,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))
}

执行代码

# 使用以下命令运行代码 可以看到两个函数计算的total值
$ go run main.go

在这里插入图片描述

仔细分析下SumInts、SumFloats函数,其内部的处理逻辑几乎一致,声明变量,遍历map,并对map内部的Value只进行累加,返回结果。唯一的区别是参数Map的类型不一致,对开发者而言,虽然粘贴-复制的工作简单,能够快速的实现以上功能。但是其价值不高,而且也会在项目中造成很多冗余的代码,有没有一种方式,可以解决这种问题呢,答案就是使用泛型。

泛型函数

在本节中,我们将添加一个泛型函数,用来接收map类型的参数,不管Value值是 integer、float类型。从而使用一个函数替换之前的两个函数,让代码更加精简。

为了支持不同的类型进行累加计算,需要从两方面进行考虑

  • 函数定义 - 需要一种方法来声明函数支持的类型
  • 函数调用 - 需要一种方法来指定函数调用的类型(使用integer or float)

函数代码

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

comparable是golang新引入的预定义标识符,comparable仅能用于泛型中的类型限定,golang中对使用comparable限制key有如下约束

  • key类型必须定义比较操作符==和!=
  • key类型必须不能是function、map或slice
  • 对于interface类型,其动态类型必须定义比较操作符
  • 不满足上述约束,则会导致运行时异常(run-time panic)

函数调用一

fmt.Printf("Generic Sums: %v and %v\n",
    SumIntsOrFloats[string, int64](ints),
    SumIntsOrFloats[string, float64](floats))

执行代码

$ go run .

在这里插入图片描述

函数调用二

在上面调用泛型函数时,指定了Map Key-Value的参数类型,其实还有一种更简单的调用方式,无需指定Map数据类型,Golang编译器也能够从函数参数中自动的进行参数推断,调用方式如下

//在main.go main函数中新增代码
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
    SumIntsOrFloats(ints),
    SumIntsOrFloats(floats))

重新执行代码,结果如下

在这里插入图片描述

类型约束

开发者可以把前面定义的约束移动到它自己的接口中,以便可以在多个地方重用它。以这种方式声明约束有助于简化代码。将类型约束声明为接口。该约束允许实现接口的任何类型。例如,如果需要在三个函数上声明类型约束接口,然后将其与泛型函数中的类型参数一起使用,则用于调用该函数的类型参数必须适用于所有方法。

声明约束

type Number interface {
    int64 | float64
}

代码说明

  • 声明要用作类型约束的Number接口类型
  • 在接口内部声明数据类型,int64 、float64

泛型函数

func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

接口调用

//在main.go main函数中新增代码
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
    SumNumbers(ints),
    SumNumbers(floats))

执行代码

$ go run .

在这里插入图片描述

完整代码

package main

import "fmt"

type Number interface {
	int64 | float64
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}
	return s
}

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

// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func SumNumbers[K comparable, V Number](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("Non-Generic Sums: %v and %v\n",
		SumInts(ints),
		SumFloats(floats))

	fmt.Printf("Generic Sums: %v and %v\n",
		SumIntsOrFloats[string, int64](ints),
		SumIntsOrFloats[string, float64](floats))

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

	fmt.Printf("Generic Sums with Constraint: %v and %v\n",
		SumNumbers(ints),
		SumNumbers(floats))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值