Go泛型的应用

一、一个简单的业务背景

我们有个业务层叫service, 两个方法,它们的返回值不同。

虽然不太一样,但是结构一样, 都是 func() (T, error)

Go 复制代码
type Service struct{}

func (s *Service) GetUser() (string, error) {
    return "alice", nil
}

func (s *Service) GetAge() (int, error) {
    return 18, nil
}

这时候假如我们需要一个"通用重试中间件",就是在业务方法调用失败的时候, 能重试一次。对应到真实业务,就好比我们每个方法都是调用第三方平台接口进行操作,如果操作失败能重试,或者token失效,能刷新一次token在重试等等。

二、非泛型方式实现

我们可以实现一个通用的中间件,它的参数就是上面的业务方法,先去执行业务方法,如果失败,在执行一次。

我们就得抽象出下面的函数类型

Go 复制代码
type BizFuncAny func() (any, error)

中间件

Go 复制代码
func WithRetry(fn BizFunc) BizFunc {
    return func() (any, error) {
        resp, err := fn()
        if err == nil {
            return resp, nil
        }

        // 模拟:失败后重试一次
        return fn()
    }
}

当要调用GetUser(),我们要手动包一层才能把GetUser()变为符合上面定义的函数类型

Go 复制代码
getUser := func() (any, error) {
    return svc.GetUser()
}

使用

Go 复制代码
func main() {
    svc := &Service{}

    // GetUser 被包成 BizFunc
    getUser := WithRetry(func() (any, error) {
        return svc.GetUser()
    })

    // GetAge 被包成 BizFunc
    getAge := WithRetry(func() (any, error) {
        return svc.GetAge()
    })

    u, _ := getUser()
    a, _ := getAge()

    // ⚠️ 必须类型断言
    fmt.Println(u.(string))
    fmt.Println(a.(int))
}

这个方式有几个缺点:

1、对于svc.GetUser等业务方法必须包一层,这样才能符合WithRetry的参数的函数签名。所以每次对新的业务方法使用的时候,都要手写闭包。

2、返回值变为any,如果我们要处理这个返回值,就得进行类型断言。

3、编译期无法发现类型错误。

4、改 DTO 会导致运行时 panic。

三、使用泛型的简单例子

我们定义一个"业务函数类型",泛型Resp:表示不同业务返回不同类型。

Go 复制代码
type BizFunc[Resp any] func() (Resp, error)

实现一个"通用重试中间件"

Go 复制代码
func WithRetry[Resp any](
    fn BizFunc[Resp],
) BizFunc[Resp] {
    return func() (Resp, error) {
        resp, err := fn()
        if err == nil {
            return resp, nil
        }

        // 这里假装"重试一次"
        return fn()
    }
}

使用这个泛型函数

Go 复制代码
func main() {
    svc := &Service{}

    getUser := WithRetry(svc.GetUser)
    getAge := WithRetry(svc.GetAge)

    u, _ := getUser()
    a, _ := getAge()

    fmt.Println(u, a) // alice 18
}
相关推荐
行百里er25 分钟前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
高频因子挖掘机1 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
鬼手点金2 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
爱上网的小舟2 小时前
星际战士2启动错误怎么办?5种游戏报错解决方法
开发语言·php
孟健2 小时前
从5000亿Token到83%字节匹配:AI逆向工程与重构的工程闭环实践
后端·架构·ai编程
阿里嘎多学长3 小时前
2026-10-12 GitHub 热点项目精选
开发语言·程序员·github·代码托管
码事漫谈3 小时前
丢得静悄悄,谈一谈 Agent 的记忆是怎么没的
后端
迪飞特科技3 小时前
智慧物业平台实战:Spring Boot 整合小程序实现报修巡检费用全流程
spring boot·后端·小程序
考虑考虑3 小时前
@JsonInclude注解使用
spring boot·后端·spring
IT_陈寒4 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端