侧边栏壁纸
博主头像
微流 - 这里记录值得分享的内容

行动起来,活在当下

  • 累计撰写 16 篇文章
  • 累计创建 9 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
Go

Go泛型:让代码更加灵活与复用

自从 Go 语言(Go version >= 1.18)发布了支持泛型的版本以来,开发者们就一直在探索如何利用这个新特性来编写更高效、更具可读性和可维护性的代码。本文将带你了解 Go 语言中的泛型是什么,为什么它如此重要,以及如何在你的项目中有效地使用它。

什么是泛型?

在编程语言中,泛型(Generics)允许我们定义一组相关的类型,而不是为每种类型都定义一个函数或结构体。这意味着,我们可以写出一个可以处理多种类型的函数,而不是针对每一种具体类型都写一个版本。这不仅减少了代码重复,而且提高了代码的灵活性。

为什么需要泛型?

在没有泛型的情况下,如果你需要实现一个排序算法,你需要为整数、字符串、自定义类型等分别写一个版本。这不仅冗余,还容易出错。有了泛型之后,你可以只写一个排序函数,然后根据传入的数据类型动态地处理。

如何使用泛型?

Go 语言通过引入[]T这样的语法来支持泛型。这里T是一个类型参数,可以在声明的时候指定具体的类型。让我们通过一个简单的例子来看看如何使用泛型。

示例:交换两个变量的值

假设我们需要写一个函数来交换两个变量的值。如果我们不使用泛型,那么对于不同的数据类型,我们需要写多个版本的函数。但是有了泛型后,我们可以这样写:

package main
​
import (
    "fmt"
)
​
// Swap 定义了一个交换两个元素值的函数
func Swap[T any](a, b *T) {
    temp := *a
    *a = *b
    *b = temp
}
​
func main() {
    var x int = 10
    var y int = 20
    Swap(&x, &y)
    fmt.Printf("x: %d, y: %d\n", x, y)
​
    var s1 string = "hello"
    var s2 string = "world"
    Swap(&s1, &s2)
    fmt.Printf("s1: %s, s2: %s\n", s1, s2)
}

在这个例子中,Swap函数接受任何类型的指针,并且可以用来交换不同类型的变量值。

提示

  • 匿名结构体不支持泛型

  • 匿名函数不支持泛型

泛型约束

虽然泛型让我们的代码变得更加灵活,但并不是所有的操作都可以对所有类型进行。例如,不是所有类型都可以进行比较或者打印。这时候就需要使用到泛型约束了。

通过在类型参数后面添加约束,我们可以限制函数的适用范围,只允许那些满足特定条件的类型使用该函数。

示例:比较两个数值

如果我们要写一个比较两个数值大小的函数,我们需要确保这两个数值是可以比较的。这可以通过添加约束来实现:

package main
​
import (
    "fmt"
)
​
// Comparable 是一个类型约束,用于表示可以比较的类型
type Comparable interface {
    int | float64 // 假设我们只比较这两种类型
}
​
// Compare 比较两个数值,并返回较大的那个
func Compare[T Comparable](a, b T) T {
    if a > b {
        return a
    }
    return b
}
​
func main() {
    fmt.Println(Compare(10, 20)) // 输出 20
    fmt.Println(Compare(10.5, 2.5)) // 输出 10.5
}

在这个例子中,我们定义了一个Comparable约束,它限制了Compare函数只能应用于intfloat64类型。

任意类型

func Add[T any](a,b T) T {
    return  a+b
}

类型嵌套

type WowStruct[T int | float32, S []T] struct {
    Data     S
    MaxValue T
    MinValue T
}
​
var ws WowStruct[int, []int]  
  • S类嵌套了T类型

通过接口实现进行约束

type Addable interface {
    int | float32
}
​
func Add[T Addable](a, b T) T {
    return a + b
}

等效于

func Add[T int | float32](a, b T) T {
    return a + b
}

使用 ~ 符号

可以使用“~”符号来指定类型参数必须是特定基本类型的一种。

type Int interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}
​
type Slice[T Int] []T 
​
var s Slice[int] // 正确
​
type MyInt int
var s2 Slice[MyInt]  // 正确。MyInt底层类型是int,所以可以用于实例化
​
type MyMyInt MyInt
var s3 Slice[MyMyInt]  // 正确。MyMyInt 虽然基于 MyInt ,但底层类型也是int,所以也能用于实例化

使用 ~ 时的限制:

  1. ~后面的类型不能为接口

  2. ~后面的类型必须为基本类型

结语

本文介绍了 Go 语言中的泛型,并展示了如何使用泛型来编写更加灵活和可复用的代码。虽然泛型带来了很多便利,但在实际开发中也要注意合理使用,避免过度抽象导致代码难以理解和维护。希望这篇文章能帮助你在 Go 项目中更好地利用泛型!

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区