Golang中泛型的详细指南(附实例)

225 阅读3分钟

今年开始,来自golang团队的新消息,"增加泛型!"

如果你熟悉Java或C#等语言,你已经熟悉了泛型的概念。例如,让我们来谈谈数组的排序问题:

package main

import (
    "fmt"
    "sort"
)

func main() {
    f := func(msg string, v []int) {
        fmt.Println(msg)
        for _, x := range v {
            fmt.Printf("%d ", x)
        }
        fmt.Println()
    }
    v := []int{10, 20, 100, 2, -1, 80}
    f("before sort", v)
    sort.Ints(v)
    f("after sort", v)
}

在上面的代码中,我们只是使用了sort 包来对一个整数片进行排序并打印出来。

一切看起来都很好,对吗?那么,如果我们想对其他数字类型的片断进行排序呢?sort 包有另一个名为Float64s 的函数(而不是Ints ),但这肯定是不够的,因为我们可能需要对其他数字类型做一些排序操作,比如int32

因此,一种方法是为每个数字类型定义一个函数,但我们都知道这不是最好的主意!这就是Generic 的地方。

以下是维基百科中对泛型的定义:

泛型编程是计算机编程的一种风格,在这种风格中,算法是以以后要指定的类型来编写的,然后在需要时对作为参数提供的特定类型进行实例化。

因此,泛型的要点是,以后指定变量的类型并将其作为参数提供。这正是我们所需要的,例如在我们的排序函数中。我们唯一需要的是确保我们的输入符合我们的条件。例如,在排序的情况下,它必须是numeric

让我们看看如何在golang中使用泛型:

func Bored[T any](s ...T) T {
    return s[0]
}

Bored 函数给出一个 的片断作为输入,并返回该片断中的第一个项目作为输出。那么WTH是 ?首先,让我们看看如何使用我们的 函数。T T Bored

func main() {
    output := Bored("Hello", "Generics")
    fmt.Println(output)
}

很好,通用函数的使用和普通函数完全一样!所以我们来谈谈T 。在这种情况下,T 就是string 。那么通用函数和老式的去interface{} 有什么区别呢?我将用一个例子来解释它:

func Bored(s ...interface{}) interface{} {
    return "I am string"
}
func main() {
    output := Bored(1,1.1,"hello", struct{}{})
    fmt.Println(output)
}

区别在于,interface{} 可以是任何东西,但在第一个函数中,T 的类型是在传递第一个元素时确定的。如果它是一个字符串,其余的也必须是一个字符串。否则,在编译应用程序时你会得到一个错误:


func Bored[T any](s ...T) T {
    return s[0]
}

func main() {
    output := Bored("Hello", "Generics", 1)
    fmt.Println(output)
}

输出:

type checking failed for main
prog.go2:11:39: default type int of 1 does not match inferred type string for T

这很好,不是吗?对于Java或C#开发人员来说,像Repository<T> 这样的东西是非常熟悉的,其中T 几乎总是应用程序的实体之一。

回到Golang ,现在引入的另一种类型(而不是any )是comparable 。这意味着该项目必须有能力进入equality operation

package main

import (
    "fmt"
)

func main() {
    fmt.Println(Equal(1, 1)) // true
    fmt.Println(Equal(2, 1)) // false
}

func Equal[T comparable] (input1, input2 T) bool {
    return input1 == input2
}

和前面的例子一样,如果向这个函数传递多个数据类型,我们会得到一个编译时间:

func main() {
    fmt.Println(Equal(1.1, 1))
}
/*
output: type checking failed for main
prog.go2:8:25: default type int of 1 does not match inferred type float64 for T
*/

我认为golang团队为开发者提供了一些方法来定义T 。目前,我只看到了anycomparable ,如果你知道更多的类型化参数,请在评论区分享它们。