Go 语言入门指南:聊聊Go中的泛型 | 青训营

80 阅读3分钟

Go语言是一门非常流行的编程语言,它以其简洁、高效和并发性而闻名。在Go 1.18版本中,引入了泛型的支持,这使得Go语言更加灵活和易于使用。本文将介绍Go中的泛型以及如何使用它们。

什么是泛型?

泛型是一种编程技术,它允许程序员编写可重用的代码,这些代码可以处理不同类型的数据,而无需为每种数据类型编写单独的代码。泛型的主要目的是提高代码的复用性和可维护性。

在Go中,泛型是通过类型参数来实现的。类型参数是一个特殊的占位符,它表示一个类型而不是值。在函数或方法的定义中,可以使用类型参数来指定该函数或方法可以接受哪些类型的参数。

如何在Go中使用泛型?

在Go中使用泛型非常简单。首先,需要在函数或方法的定义中使用类型参数。例如,下面是一个使用泛型的函数示例:

func PrintSlice[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}  

在这个例子中,T 是一个类型参数,any 是一个预定义的类型,表示任何类型都可以作为类型参数的值。这个函数可以接受任何类型的切片作为参数,并打印出其中的元素。

要调用这个函数,只需要传递一个切片作为参数即可。例如:

ints := []int{1, 2, 3, 4, 5}
PrintSlice(ints) // 输出:1 2 3 4 5

除了使用内置的 any 类型之外,还可以使用自定义的类型作为类型参数。例如:

type Person struct {
    Name string
    Age  int
}

func PrintPerson(p Person) {
    fmt.Println("Name:", p.Name)
    fmt.Println("Age:", p.Age)
} 

在这个例子中,Person 结构体被用作 PrintPerson 函数的类型参数。这个函数可以接受任何类型的 Person 对象作为参数,并打印出其中的信息。

什么情况不宜使用泛型

首先,如果要对某一类型的值进行的全部操作,仅仅是在那个值上调用一个方法,使用interface类型,而不是类型参数。比如,io.Reader易读且高效,没有必要像下面代码中这样使用一个类型参数像调用Read方法那样去从一个值中读取数据:

func ReadAll[reader io.Reader](r reader) ([]byte, error)  // 错误写法
func ReadAll(r io.Reader) ([]byte, error)                 // 正确写法

使用类型参数的原因是它们让代码更清晰, 如果它们会让代码变得更复杂,就不要使用

第二,当不同的类型使用一个共同的方法时,如果一个方法的实现对于所有类型都相同,就使用类型参数;相反,如果每种类型的实现各不相同,请使用不同的方法,不要使用类型参数。

最后,如果发现自己多次编写完全相同的代码,各个版本之间唯一的差别是代码使用不同的类型,那就要考虑是否可以使用类型参数。反之,在注意到自己要多次编写完全相同的代码之前,应该避免使用类型参数。

泛型的局限性

虽然泛型可以使代码更加灵活和易于使用,但它也有一些局限性。首先,泛型会降低代码的性能。由于编译器需要对每个泛型函数进行类型检查,因此在使用泛型时可能会增加运行时的开销。其次,泛型可能会导致代码变得更加复杂和难以理解。如果使用不当,泛型可能会导致代码出现错误或不可预测的行为。因此,在使用泛型时需要注意其使用方法和限制。