likes
comments
collection
share

[Golang] 泛型的使用

作者站长头像
站长
· 阅读数 84

前言

  泛型 是什么? 其实这个问题对于学过 C++ 的同学来说不陌生就是模版嘛, 在运行时才确定类型嘛. 确实这两句话就基本能解析清楚, 但像一些只接触过 Python 的同学来说确实没听说过, 毕竟就是 Python 是动态编译的不需要泛型.

  对于 Golang 来说, 泛型在牺牲一点性能的前提下能大大降低对通用代码的编码难度, 不然只能乖乖的用反射解决问题, 或者对不同类型多复制出几份代码;

  本文没有对泛型进行入门介绍, 需要的请跳转 泛型初识.

泛型实践

关键字参数

  • 众所周知很多语言的function 中都支持 key=word 关键字参数, 但 golang 是不支持的, 我们可以利用泛型去简单的实现.

    func DefaultKeyWordParams[D any](defVal D, params ...D) D {
            if len(params) == 0 {
                    return defVal
            }
            return params[0]
    }
    
    
    func test(category ...string) {
        // 不填写则返回默认值
        realCategory := DefaultKeyWordParams[string]("AGroup", category...)
        fmt.Println(realCategory)
    }
    
    func main () {
         test()
    }
    

去重复

  • 这是一个简单的实现, 复杂点可以通过回调 + 泛型来实现;

    func RemoveDuplicate[T string | int | float64](duplicateSlice []T) []T {
            set := map[T]interface{}{}
            res := []T{}
            for _, item := range duplicateSlice {
                    _, ok := set[item]
                    if !ok {
                            res = append(res, item)
                            set[item] = nil
                    }
            }
            return res
    }
    
    func main() {
            fmt.Println(RemoveDuplicate[string]([]string{"a", "c", "a"}))
            fmt.Println(RemoveDuplicate[int]([]int{1, 2, 1, 1, 1}))
    }
    
  • 通过控制反转实现通用的去重复方法, 支持任意类型;

    type Student struct {
            Name string
            Age  int
    }
    
    func NewStudent(name string, age int) *Student {
            return &Student{Name: name, Age: age}
    }
    
    func DefaultFilter(item interface{}) (uniqueKey interface{}) {
            return item.(*Student).Name
    }
    
    func RemoveDuplicateWithFilter[T comparable](compareSlice []T, filterFunc func(item interface{}) (key interface{})) []T {
            set := map[interface{}]interface{}{}
            res := []T{}
            for _, item := range compareSlice {
                    i := filterFunc(item)
                    _, ok := set[i]
                    if !ok {
                            res = append(res, item)
                            set[i] = nil
                    }
            }
            return res
    }
    
    func main() {
            s := []*Student{
                    NewStudent("a", 1),
                    NewStudent("a", 1),
                    NewStudent("b", 2),
                    NewStudent("b", 2),
            }
            l := RemoveDuplicateWithFilter[*Student](s, DefaultFilter)
            for _, i := range l {
                    fmt.Println(i.Name, i.Age)
            }
    }
    

联合约束类型

  • 该例子只是一个演示, 没有实际效果
    type ID interface {
        int | string
    }
    
    // 写法  [T ID, D string] == [T int | string, D string]
    type UserModel[T ID, D string] struct {
            Id   T
            Name D
    }
    
    func NewUserModel[A ID, D string](id A, name D) *UserModel[A, D] {
            return &UserModel[A, D]{Id: id, Name: name}
    }
    
    func main() {
            fmt.Println(NewUserModel[int, string](10, "hello"))
            fmt.Println(NewUserModel[string, string]("10", "hello"))
    }
    

分页

  • 这是一段线上在使用的分页代码, 当无法使用外部存储器进行分页时直接使用该对象进行分页, 支持任意类型;
    type KeepItem bool
    
    // 若需要保留的item 则返回true 即可
    type FilterFunc func(item interface{}) KeepItem
    
    type PageList[T any] struct {
            Total int `json:"total"`
            Page  int `json:"page"`
            Size  int `json:"size"`
            List  []T `json:"list"`
    }
    
    type Pager[T any] struct {
            limit   int
            offset  int
            total   int
            pageCnt int
            list    []T
    }
    
    func NewPager[T any](list []T) *Pager[T] {
            return &Pager[T]{
                    limit:  10,
                    offset: 1,
                    total:  len(list),
                    list:   list,
            }
    }
    
    func (this *Pager[T]) Filter(filterFn FilterFunc) *Pager[T] {
            tmpList := []T{}
            for _, item := range this.list {
                    if filterFn(&item) {
                            tmpList = append(tmpList, item)
                    }
            }
            this.list = tmpList
            this.total = len(tmpList)
            return this
    }
    
    func (this *Pager[T]) Offset(c int) *Pager[T] {
            this.offset = c
            return this
    }
    
    func (this *Pager[T]) Limit(c int) *Pager[T] {
            this.limit = c
            return this
    }
    
    func (this *Pager[T]) List() []T {
            // 页码
            if this.offset <= 0 {
                    this.offset = 1
            }
            // size
            if this.limit > this.total {
                    this.limit = this.total
            }
            // 总页数
            this.pageCnt = int(math.Ceil(float64(this.total) / float64(this.limit)))
            if this.offset > this.pageCnt {
                    return []T{}
            }
            startIdx := (this.offset - 1) * this.limit
            endIdx := startIdx + this.limit
    
            if endIdx > this.total {
                    endIdx = this.total
            }
    
            return this.list[startIdx:endIdx]
    }
    
    func (this *Pager[T]) Output() *PageList[T] {
    
            return &PageList[T]{
                    Total: this.total,
                    Page:  this.offset,
                    Size:  this.limit,
                    List:  this.list,
            }
    }
    
    // test
    func main () {
    	page := NewPager[int]([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
            list := page.Offset(1).Limit(3).Filter(func(item interface{}) KeepItem {
    	if *item.(*int)%2 == 1 {
    		return true
    	}
    	return false
            }).List()
            fmt.Println(list)
    }
     
    

通用初始化模型

  • 可以解决在多态下使用同一个初始化函数进行对象初始化, 写法上有点绕大家自行多实验几次就能明白.
    type ModelObj interface {
            User | Product
    }
    
    type User struct {
            Uid int
    }
    
    func (this *User) SetId(id int) {
            this.Uid = id
    }
    
    type Product struct {
            Pid int
    }
    
    func (this *Product) SetId(id int) {
            this.Pid = id
    }
    
    // TrimModelObj 是一个动态类型的 Interface, 由M决定当前Interface的最终类型
    type TrimModelObj[M ModelObj] interface {
            *M
            SetId(id int)
    }
    
    // TrimModelObj[Model] 由第二个参数决定当前的动态类型;
    // NewModelObj[*User, User](32) 如 Model 是 User 类型, 最终 TrimModelObj == *User,所以我们需要为 Trim 传递 *User
    func NewModelObj[Trim TrimModelObj[Model], Model ModelObj](id int) Trim {
            m := new(Model)
            t := Trim(m)
            fmt.Printf("%p \n", m)
            // 类型转换成指定的*Model
            t.SetId(id)
            return t
    }
    
    
    func main() {
            // new user model object
            user := NewModelObj[*User, User](32)
            fmt.Printf("%p \n", user)
            fmt.Printf("%T \n", user)
            fmt.Println(user.Uid)
    
            // new product model object
            prod := NewModelObj[*Product, Product](18)
            fmt.Printf("%p \n", prod)
            fmt.Printf("%T \n", prod)
            fmt.Println(prod.Pid)
    }
    

写在最后

   2022年总结没时间码, 那么2023年就祝大家年年有余(摸鱼)、身体健康, 新的一年继续搬砖; 🐰🎉🎉🎁🎁🪅🪅

转载自:https://juejin.cn/post/7194468198270861368
评论
请登录