Go 语言泛型使用详解

开发 前端
本文我们回顾了 Go v1.18 引入的泛型的语法和使用方式,截止目前,虽然 Go 已经迭代了 5 个版本,泛型仍然未得到广泛使用。

1.介绍

Go v1.18 开始支持泛型,距离 Go 当前版本 v1.23 已经迭代了 5 个大版本了。读者朋友们在使用 Go 语言开发时,是否已经习惯使用泛型了呢?

本文我们一起再来回顾学习一下 Go 语言中的泛型。

2.概念和语法

概念

Go 语言引入泛型,主要目的是减少代码重复,提高类型安全。泛型是指在定义函数(方法)、类型或数据结构时,使用类型参数来表示具体的类型,从而提高代码的灵活性和可用性。

所谓灵活性,即不需要为每种类型编写相似代码。所谓可用性,即在编译时检查类型,避免运行时错误。

语法

所谓类型参数,即类型本身也可以作为一种参数。在函数(方法)或类型中,可以使用类型参数定义通用类型,使用方括号 [] 包含任意合法的标识符。

示例代码:

[T int|string]

阅读上面这段代码,T 是类型参数的形式参数,int、string 是类型参数的类型约束。

所谓类型约束,实际上是一个 interface{},通常可以省略,但是,如果类型约束特别多时,也可以先定义类型集,再使用,并且可以复用。

示例代码:

// 定义
type MyType interface{
    int|int8|int32|int64|float32|float64
}

// 使用
[T MyType]

阅读上面这段代码,与以往不同, interface{} 中不再是函数集,而是类型集。也就是说,在 Go v1.18 开始,interface{} 不仅可以定义函数集,也可以定义类型集。

需要注意的是,Go v1.18 开始,interface{} 不仅可以包含任意类型,还可以包含任意类型集,或共享相同底层类型的所有类型。

示例代码:

type A interface{
    int|float64
}

type B interface{
    string|bool
}

type C interface{
    A|B
}

type MyString ~string

阅读上面这段代码,~string 代表 string 类型本身,和以 string 类型为底层类型的所有类型。

3.使用方式

在了解完泛型的概念和语法之后,接下来,我们介绍泛型的使用方式。

泛型类型

切片 slice 示例代码:

type Sl [T int|float64] []T

映射 map 示例代码:

type M [K string|int, V string|int] map[K]V

阅读上面这段代码,[] 中包含多个类型参数,需要使用英文逗号 , 分隔,并且类型参数的形式参数名字不能相同。

结构体 struct 示例代码:

type St [T int|string] struct {
    id     int
    name   string
    salary T
}

需要注意的是,以上所有泛型类型,在使用的时候,需要显式指定类型的实参(类型约束),因为它不支持类型推断。

以结构体 struct 为例,示例代码:

coder := &St[int]{
    id:     1,
    name:   "frank",
    salary: 1000,
}
fmt.Printf("%+v\n", coder)

泛型方法

接下来,我们介绍泛型类型的泛型方法,示例代码:

type Salary[T int|float64] struct {
    X,Y T
}

func (s *Salary[T]) Min(x, y T) T {
    if x < y {
        return x
    }
    return y
}

// 显式指定类型参数的实际参数(类型约束),不支持类型推断
sa := &Salary[int] {
    X: 1000,
    Y: 2000,
}
value := sa.Min(sa.X, sa.Y)
fmt.Println(value)

需要注意的是,泛型方法的入参不支持自定义类型参数,示例代码:

func (s *Salary[T]) Min[T1 int32|float32](x, y T1) T {
    if x < y {
        return x
    }
    return y
}

阅读上面这段代码,泛型方法的入参,自定义了类型参数 Min[T1 int32|float32](x, y T1),目前是不支持。

泛型函数

接下来,我们介绍泛型函数的使用方式,示例代码:

func MinNumber[T int|float64](x, y T) T {
    if x < y {
        return x
    }
    return y
}

r := MinNumber[int](1, 2)

阅读上面这段代码,我们使用类型参数定义的函数,就是泛型函数。需要注意的是,在使用函数时,我们显式指定函数入参的类型 r := MinNumber[int](1, 2),实际上,可以通过类型推断,通过函数的入参推断泛型的实际类型。即 r := MinNumber(1, 2)。

需要注意是,泛型函数的类型推断,仅支持函数的入参,函数的返回结果和函数体是不支持的。

4.总结

本文我们回顾了 Go v1.18 引入的泛型的语法和使用方式,截止目前,虽然 Go 已经迭代了 5 个版本,泛型仍然未得到广泛使用。

在 Go 未推出泛型之前,Go 社区的呼声很大,Go 引入泛型之后,未能得到广泛使用的原因是一些三方库和框架,为了追求稳定,不愿意大改代码。

其次,泛型虽然优势明显,同时也带来的 Go 语法的复杂性,这一点有悖于 Go 推崇的使用简单。

对此我的看法是,建议读者朋友们积极学习和使用泛型,老项目如果不愿意重构,建议新项目开始使用泛型。

责任编辑:武晓燕 来源: Golang语言开发栈
相关推荐

2021-09-29 18:17:30

Go泛型语言

2022-03-28 13:34:26

Go泛型部署泛型

2021-10-18 10:53:26

Go 代码技术

2021-01-14 05:20:48

Go语言泛型

2021-12-13 08:52:42

Go 泛型

2022-04-15 09:55:59

Go 泛型Go 程序函数

2022-03-18 18:00:00

编程语言泛型支持模糊测试

2021-06-18 08:25:42

Java泛型通配符

2022-05-06 09:22:25

Go泛型

2021-11-27 22:20:13

SlicesGo泛型

2023-11-03 14:02:04

Go切片泛型库

2023-11-29 08:19:45

Go泛型缺陷

2011-04-13 09:16:55

泛型

2021-12-15 10:23:56

Go 1.18 Bet语言泛型

2021-12-05 23:45:23

Go泛型Maps

2009-07-30 14:00:21

ASP.NET 2.0

2021-06-17 06:51:32

Java泛型Java编程

2023-09-15 21:05:19

Go语言参数化

2011-03-21 16:26:28

java泛型

2022-03-29 11:48:40

Go泛型测试
点赞
收藏

51CTO技术栈公众号