前言

泛型在很多面向对象的编程中大多是有支持的,比如c++,java,python。泛型可以解决相同实现但是类型不同导致编码重复的问题,在Go的早期版本是不支持泛型的,这也是被很多人拿来吐槽Go的一个点。早期Go中可以使用接口类型加上断言或者反射来实现泛型编程,使用反射又会拉低Go的执行效率。所以Go在1.18版本加入了对泛型的支持,来优化使用反射比较麻烦,编译时检查不出错误,性能较差。泛型可以减少重复代码增加安全性,针对不通类型书写同样的逻辑可以使用泛型简化代码的问题。

Go泛型特点和语法

定义泛型类型

如声明一个泛型切片类型:

func main() {
    type MySlice[T int | string] []T
    is := MySlice[int]{1, 2, 3}
    fmt.Println(is)
}

定义map泛型类型:

    type MyMap[KEY int | string, VALUE string] map[KEY]VALUE
    m := MyMap[int, string]{1: "v1"}
    fmt.Printf("type:%T,m:%+v", m, m)

定义泛型结构体:

    type MyStruct[T int | string] struct {
        id   T
        name string
    }

    ms := MyStruct[string]{
        id:   "1",
        name: "redhat",
    }
    fmt.Printf("type:%T,m:%+v\n", ms, ms)

定义泛型接口:

package main

import "fmt"

type printer[T any] interface {
    print(T)
}

type stringPrinter struct{}

func (s stringPrinter) print(v string) {
    fmt.Println(v)
}

func print[T any](sp printer[T], v T) {
    sp.print(v)
}

func main() {
    sp := stringPrinter{}
    print(sp, "nihao")
}

定义泛型管道:

package main

import (
    "fmt"
    "time"
)

type myChan[T any] chan T

func testChan[T any](c myChan[T]){
    for k := range c {
        fmt.Println(k)
    }
}

func main() {
    c := make(myChan[string])
    go testChan(c)

    c <- "1"
    c <- "2"

    a := make(myChan[int])
    go testChan(a)

    a <- 1
    a <- 2

    time.Sleep(time.Duration(2) * time.Second)
}

定义泛型接收者

package main

import (
    "fmt"
)

type MySlice[T int | string] []T

func (s MySlice[T])sum() T{
    sum:=T(0)
    for _,v:=range s{
        sum+=v
    }

    return sum
}

func main() {
    is := MySlice[int]{1, 2, 3}
    fmt.Println(is.sum())

    ic := MySlice[string]{"1", "2", "3"}
    fmt.Println(ic.sum())
}

接收者这里可以直接使用T。

定义限制泛型类型

go原生提供了两个限制类型,一个是any一个是comparable,也可以自定义限制类型:

package main

import (
    "fmt"
)

type mygeric interface{
    int | string
}

type MySlice[T mygeric] []T

func (s MySlice[T])sum() T{
    sum:=T(0)
    for _,v:=range s{
        sum+=v
    }

    return sum
}

func main() {
    is := MySlice[int]{1, 2, 3}
    fmt.Println(is.sum())

    ic := MySlice[string]{"1", "2", "3"}
    fmt.Println(ic.sum())
}

使用泛型实现set

package main

import "fmt"

type Set[T comparable] map[T]struct{}

func (s *Set[T]) Set(key T) {
    (*s)[key] = struct{}{} //这里注意解引用
}

func (s *Set[T]) Lens() int {
    return len(*s)
}

func (s *Set[T]) Range() []T {
    var nums []T
    for key := range *s {
        nums = append(nums, key)
    }
    return nums
}

func (s *Set[T]) IsInside(key T) bool {
    _, ok := (*s)[key]
    return ok
}

func (s *Set[T]) Delete(key T) {
    delete(*s, key)
}

func CreateSet[T comparable]() *Set[T] {
    m := make(Set[T])
    return &m //这里使用new的话会返回空map,不可以直接写操作
}

func main() {
    m := CreateSet[int]() //这里注意函数调用
    m.Set(1)
    m.Set(2)
    fmt.Println(m.Lens())
    fmt.Println(m.Range())
    fmt.Println(m.IsInside(1))
    m.Delete(1)
    fmt.Println(m.IsInside(1))
}

总结

泛型,类比形式参数,其实泛型就是形式类型,函数中使用形式参数传参,调用时使用实参。类型也是,定义时为形式类型,实际使用时要指定实际类型。类型形参和类型实参概念。

--EOF

最后修改:2025 年 02 月 25 日
如果觉得我的文章对你有用,请随意赞赏