Golang 泛型学习

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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))
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go