Go

12.泛型

2025-08-25  本文已影响0人  Surpassme

    Go语言在v1.18版本添加。其中泛即通用的意思

12.1 泛型函数

    如果没有泛型,同一种类型需要使用重载功能,但Go又不支持重载功能。如果写一个简单的加法计算函数,我们只能定义出如下所示的代码:

func AddInt(a, b int) int {
    return a + b
}

func AddFloat(a, b float64) float64 {
    return a + b
}

func JoinString(a, b string) string {
    return a + b
}

    从上面代码可以看出,每个函数的参数个数都一样,仅仅参数的数据类型不一样,如果还有类似的功能,仅仅是参数数据类型不一致,就还得增加函数,从而导致代码存在大量的重复。那么有没有一种,可以将参数的数据类型再抽象一次来解决这样的问题,我们来看看以下代码

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

    上面代码中的T表示类型形参,即参数的数据类型是可变的,a,b T则表明a和b的类型要一致。即在运行时,T最终一定会确定是某一种数据类型。Go语言针对这一问题的解决方案是泛型。其语法格式如下所示:

func name[T,P](parameters-1 T,parameters-2 P) P{
    函数语句块
}

    示例代码如下所示:

package main

import "fmt"

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

func main() {
    fmt.Printf("int: %v,%[1]T\n", Add(4, 5))
    fmt.Printf("float: %v,%[1]T\n", Add(4.7, 5.8))
    fmt.Printf("string: %v,%[1]T\n", Add("Sur", "pass"))
}

    运行结果如下所示:

int: 9,int
float: 10.5,float64
string: Surpass,string

    通过以上代码,可以看到代码量大大减小,而且也非常简洁。

  • Add[int](4,5) 可以简写为 Add(4,5),因为可以根据传入的参数自动推断出数据类型
  • 定义泛型函数时,函数名后面紧跟着类型参数列表。因此匿名函数不可以定义为泛型函数,但可通过使用定义的类型开通T

12.2 类型约束

    类型约束是一个接口。为支持泛型,Go v1.18对接口进行了语法扩展。用在泛型中,接口含义就是符合这些特征的类型集合。在Go语言中,内置了两个约束:

[T int ] 等价于[T interface{int}] // 表示T只能是int类型

type Constraint interface{
    int | string
}

[T int | string ] 、 [ T interface{int | string }] 和 [T Constraint]  三者等价,都表示类型只能是int或string类型

12.3 泛型类型

    示例代码如下所示:

package main

import "fmt"

// 接口从本质上讲,就是一种类型约束,如果要使用接口类型,则要求其结构必须接口中所有的方法
type Runer interface {
    Run()
}

// 定义一种新的Map类型,要求Key的数据类型为int或string,而value为Runner类型
// [K string | int, V Runer] 对自定义类型的参数进行约束
type MyMap[K string | int, V Runer] map[K]V

type MyString string // 类型新定义

func (myString MyString) Run() {
    fmt.Printf("MyString:%#v - %[1]T\n", myString)
}

func main() {
    // var m MyMap[string, MyString] = make(MyMap[string, MyString])
    d := make(MyMap[string, MyString]) //相当于map[string]MyString{}
    fmt.Printf("d: %#v - %[1]T\n", d)
    d["name"] = "Surpass"
    fmt.Printf("d: %#v - %[1]T\n", d)
    d["name"].Run()
}

    运行结果如下所示:

d: main.MyMap[string,main.MyString]{} - main.MyMap[string,main.MyString]
d: main.MyMap[string,main.MyString]{"name":"Surpass"} - main.MyMap[string,main.MyString]
MyString:"Surpass" - main.MyString

    结合以上代码,泛型是对函数的参数或返回值设置多个数据类型,比普通函数更灵活的设置参数类型和返回值类型,但却又比不上空接口参数的开放自由。

    既然泛型比不上空接口,那为什么还要引入泛型呢?我们知道空接口参数不受数据类型限制,但如果在调用过程中,函数传入的参数是无法处理的数据类型,则很容易导致出现异常情况。而使用泛型时,既可以对函数的参数和返回值数据类型进行约束,也能保证传入参数的多样性

上一篇 下一篇

猜你喜欢

热点阅读