Golang学习笔记(八)—— 泛型

发布时间 2024-01-08 22:33:19作者: 昨晚没做梦

泛型


泛型是什么?为什么要用泛型?

  在开发中,会有一些复用性很强的功能,它被应用到很多地方,但为了适用会被不断重写,这很低效。例如一个intADD函数,他能进行 int 的加法,假如我们想进行 float 加法,又得重写一个floatADD函数,泛型就是为了解决这个问题而推出的功能。

  想要接收多种类型的参数,可以使用空接口来接收,在函数里面进行类型断言后实现操作。这是泛型出现前的做法,虽然没那么低效,但也称不上高效,使用泛型更加高效。

 

泛型函数语法

func func_name[T 类型1 | 类型2 | ... ,类型参数...](形参 T, ...) 返回值 {
    //...
}

  相比普通函数,泛型多了一个类型参数,和其他语言不同,Go用中括号[ ] 来装载类型参数,而不是尖括号< >。

 

//举个例子

func add[T int | float64 | float32, Q string | int | float32 | float64](a, b T, c Q) T {
    fmt.Println(c)
    return a + b
}

func main() {
    add(3, 3.14, "Go")

}

类型参数

  上述例子中, T 和 Q 都是类型参数,可以有多个类型参数;而跟在类型参数后面的是类型约束,也就是限定类型的范围。

  我们知道内置类型是不能自定义方法的,所以有的时候我们会基于一个内置类型来自定义类型,例如 type myint int,每有一个这种自定义类型,泛型的类型参数又多一个,这很繁琐,而且不好看。

  因此 Go 使用 ~ 来表示底层类型一致,例如 ~int 可以代表 int 和 myint,以及所有 只基于int的自定义类型。

 

类型集

  可以预见的是,如果类型参数的类型约束很多,那么代码的可读性会很差。因此 Go 将接口扩展,能够通过接口定义类型集 来进行类型约束。

type number interface {
    ~int | ~float64 | ~float32
}

type pmsg interface {
    ~string | ~int | ~float32 | ~float64
}

func add[T number, Q pmsg](a, b T, c Q) T {
    fmt.Println(c)
    return a + b
}

func main() {
    add(3, 3.14, "Go")

}

 

泛型类型

语法如下:

 type TypeName[T Type] struct {
     // ...
 }

举个例子:

package main

import "fmt"

type Stack[T any] struct {
    data []T
}

func (s *Stack[T]) Push(x T) {
    s.data = append(s.data, x)
}

func (s *Stack[T]) Pop() T {
    n := len(s.data)
    x := s.data[n-1]
    s.data = s.data[:n-1]
    return x
}

func main(){
    s := Stack[int]{}
    for i:=1;i<=5;i++{
        s.push(i)
    }
    fmt.Println(s.Pop())
    fmt.Println(s.Pop())
}

 

泛型接口

package main

import (
    "fmt"
)

type Person[T any] interface {
    Say(T) T
}

type Student[T any] struct {
    Name string
}

func (s *Student[T]) Say(t T) T {
    fmt.Println(s.Name, "say:", t)
    return t
}

type Teacher[T any] struct {
    Name string
}

func (s *Teacher[T]) Say(t T) T {
    fmt.Println(s.Name, "say:", t)
    return t
}

func main() {
    var p1 Person[string] = &Student[string]{Name: "Tom"}
    fmt.Println("main:", p1.Say("ccc"))
    var p2 Person[int] = &Teacher[int]{Name: "Ann"}
    fmt.Println("main:", p2.Say(123))
}

 

小结

  1. 泛型的使用范围并没有想象中那么大,一般来说,实现通用的数据结构不同类型需要实现公用方法 时才会用到。
  2. 虽然很相似,但是不要将 interface 类型全用泛型替换,这样性能并不会更好。
  3. 不要滥用泛型。