Go 的范型怎么把 Response[指定类型] 转换为 Response[any] - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
jorneyr
V2EX    程序员

Go 的范型怎么把 Response[指定类型] 转换为 Response[any]

  •  
  •   jorneyr 2023-01-28 10:52:36 +08:00 3656 次点击
    这是一个创建于 985 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题: Go 的范型怎么把 Response[指定类型] 转换为 Response[any].

    下面的示例代码在 [3] 部分进行类型转换的时候报错 cannot use rsp2 (variable of type Response[map[string]string]) as Response[any] value in assignment 。

    package main import ( "encoding/json" "fmt" ) type Response[T any] struct { Code int `json:"code"` // 业务逻辑相关的 code ,不是 HTTP Status Code Success bool `json:"success"` // 业务逻辑处理成功时为 true ,错误时为 false Msg string `json:"msg"` // 请求的描述 Data T `json:"data,omitempty"` // 请求的 payload } func main() { } func foo[T any]() Response[any] { // [1] 创建范型对象 rsp1 := Response[map[string]string]{ Code: 200, Success: true, Data: map[string]string{"1": "one", "2": "two"}, } // [2] 范型序列化 b, _ := json.Marshal(rsp1) fmt.Println(string(b)) // [3] 范型反序列化 var rsp2 Response[map[string]string] json.Unmarshal(b, &rsp2) fmt.Printf("%+v\n", rsp2) // [4] 范型向上类型转换 // 相当于 Java 的范型用 ? 来接收任意类型的范型如 // List<String> list1 = new LinkedList<>(); // List<?> list2 = list1; rsp3 := Response[any]{} rsp3 = rsp2 // 错误: cannot use rsp2 (variable of type Response[map[string]string]) as Response[any] value in assignment return rsp3 } 
    29 条回复    2023-01-29 15:34:21 +08:00
    nobot
        1
    nobot  
       2023-01-28 11:01:06 +08:00
    返回值需要明确知道类型,不然编译器,无法特例化对应类型的函数
    jorneyr
        2
    jorneyr  
    OP
       2023-01-28 11:05:21 +08:00
    @nobot 还不到返回值类型的地方,rsp3 = rsp2 赋值的时候就报错了。
    nobot
        3
    nobot  
       2023-01-28 11:06:23 +08:00
    返回 Response[T]
    @jorneyr
    hahadaxigua834
        4
    hahadaxigua834  
       2023-01-28 11:12:40 +08:00
    func (x *Response[T]) ToAny() Response[any] {
    return Response[any]{
    Code: x.Code,
    Success: x.Success,
    Msg: x.Msg,
    Data: x.Data,
    }
    }
    jorneyr
        5
    jorneyr  
    OP
       2023-01-28 11:12:58 +08:00
    @nobot 我想定义一个函数,接收任意类型的 Response[any],然后处理后返回给客户端,所以这个函数的目录就是接收任意类型的 Response ,也就是上面用 rsp3 = rsp2 进行演示。

    返回 Response[T] 满足不了需求。
    rrfeng
        6
    rrfeng  
       2023-01-28 11:17:11 +08:00
    你都上泛型了为啥要 any……是不是跑偏了
    kaf
        7
    kaf  
       2023-01-28 11:17:14 +08:00
    编译错误说明了 Response[map[string]string]和 Response[any]是两个不同类型的参数
    jorneyr
        8
    jorneyr  
    OP
       2023-01-28 11:17:29 +08:00
    @hahadaxigua834
    谢谢,返回前调用这个方法转换一下能满足要求。
    hahadaxigua834
        9
    hahadaxigua834  
       2023-01-28 11:18:07 +08:00
    @hahadaxigua834 最快的方法就是做个 copy
    jorneyr
        10
    jorneyr  
    OP
       2023-01-28 11:18:29 +08:00
    @kaf Java 的范型 ? 可以接收任意类型的,就想看看 Go 里能不能也用相似的办法实现。
    kaf
        11
    kaf  
       2023-01-28 11:21:57 +08:00
    @jorneyr
    func foo[T any](in T) Response[T] {
    // [1] 创建范型对象
    rsp1 := Response[T]{
    Code: 200,
    Success: true,
    Data: in,
    }
    return rsp1
    }
    你是说这样的功能吗,go 的泛型使用就是你确定入参和返回类型的情况下
    jorneyr
        12
    jorneyr  
    OP
       2023-01-28 11:28:46 +08:00
    @kaf
    // 像下面这样,接收 Response[string], Response[int] 等任意类型的范型参数进行统一处理。
    // 业务代码里可能生成 Response[string], Response[AgentStats] 等不同类型的响应对象,这些对象都会在下面的 responseCommonHandle 函数中统一处理例如某些情况下打印日志。
    func responseCommonHandle(rsp Rsponse[any]) {

    }
    OuJin
        13
    OuJin  
       2023-01-28 11:28:48 +08:00
    @jorneyr

    func main() {

    // 返回数据
    var (
    response1 = Response[map[string]string]{} // 返回 1, 数据类型 map[string]string
    response2 = Response[map[int]int]{} // 返回 2, 数据类型 map[int]int
    // ...
    )

    // 模拟接收到的数据
    var (
    body1, _ = json.Marshal(response1)
    body2, _ = json.Marshal(response2)
    )

    // 解析
    var (
    result1, err1 = foo[map[string]string](body1) // 指定 T 类型为 map[string]string
    result2, err2 = foo[map[int]int](body2) // 指定 T 类型为 map[int]int
    )

    fmt.Println(result1, err1)
    fmt.Println(result2, err2)
    }

    func foo[T any](body []byte) (response Response[T], err error) {
    err = json.Unmarshal(body, &response)
    return
    }

    在调用 foo 时指定类型,看看这样满不满足要求
    jorneyr
        14
    jorneyr  
    OP
       2023-01-28 11:36:53 +08:00
    @OuJin 谢谢,我的问题重点不是在序列化和反序列化方面 (提问的时候应该去掉,加上只是为了验证序列化功能在范型的时候可以正常使用)。

    我的问题主要是在不知道 Go 里有没有一个像 Java 范型那样: 定一个范型类型,可以接收任意类型的范型对象,也就是下面这个例子:
    List<String> list1 = new LinkedList<>();
    List<?> list2 = list1;
    bigboNed3
        15
    bigboNed3  
       2023-01-28 11:40:21 +08:00
    @jorneyr
    ```
    // [4] 范型向上类型转换
    // 相当于 Java 的范型用 ? 来接收任意类型的范型如
    // List<String> list1 = new LinkedList<>();
    // List<?> list2 = list1;
    rsp3 := Response[any]{Code: rsp2.Code, Success: rsp2.Success, Data: rsp2.Data}

    ```
    kaf
        16
    kaf  
       2023-01-28 11:40:22 +08:00
    @jorneyr 下面这段代码是否是你想要的功能
    ```
    package main

    // 定义一个结构体,Data 是一个泛型接口
    type Response[T ResponseHandle] struct {
    Code int `json:"code"` // 业务逻辑相关的 code ,不是 HTTP Status Code
    Success bool `json:"success"` // 业务逻辑处理成功时为 true ,错误时为 false
    Msg string `json:"msg"` // 请求的描述
    Data T `json:"data,omitempty"` // 请求的 payload
    }

    type ResponseHandle interface {
    log()
    }

    type AgentStats struct {
    Status int
    }

    func (r *AgentStats) log() {
    // do something
    }

    func main() {

    }

    // 输入泛型的 resp,在函数中执行相关的方法
    func responseCommonHandle[T ResponseHandle](rsp Response[T]) {
    rsp.Data.log()
    }
    ```
    jorneyr
        17
    jorneyr  
    OP
       2023-01-28 11:44:35 +08:00
    @kaf 不是的,如果用接口的方式,每个类型都要实现接口,反而工作量更大了,只是想要一个纯粹的存放数据的结构体。
    lysS
        18
    lysS  
       2023-01-28 11:47:47 +08:00
    T[any] 这种泛型没啥意义,还有进一步的性能损失,不如直接 any 断言
    kaf
        19
    kaf  
       2023-01-28 11:51:38 +08:00
    @jorneyr 那其实输出类型 T ,返回类型 T 即可,你应该使用 T 类型而不是使用 any ,any 只是封装的 interface 类型,go 的泛型并不是 Java 的泛型,Java 的所有对象继承于 Object ,在 go 中每个结构体都是单独的类型,并不能强转,而且你需要在函数定义是知道输入什么类型,类似于 interface 可以接受任意类型参数,而定义泛型之后,编译器知道了 interface 是你定义的泛型结构体中的一个
    kaf
        20
    kaf  
       2023-01-28 11:54:32 +08:00
    @jorneyr 可以看 go 实现 Java 的 stream 的 map 方法
    // Map manipulates a slice and transforms it to a slice of another type.
    // Play: https://go.dev/play/p/OkPcYAhBo0D
    func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
    result := make([]R, len(collection))

    for i, item := range collection {
    result[i] = iteratee(item, i)
    }

    return result
    }
    we8105
        21
    we8105  
       2023-01-28 14:13:43 +08:00
    我也有类似的问题
    jorneyr
        22
    jorneyr  
    OP
       2023-01-28 14:27:36 +08:00
    @kaf 与你的思路还是不太一样的。
    exonuclease
        23
    exonuclease  
       2023-01-28 14:51:39 +08:00
    Response<T>不能直接用吗 c#里面是可以的啊 比如这个函数
    private static TValue getValue<Tkey,TValue>(KeyValuePair<Tkey,TValue> kvp)
    {
    return kvp.Value;
    }
    jorneyr
        24
    jorneyr  
    OP
       2023-01-28 15:19:26 +08:00
    @exonuclease 在 go 里不可以呢。
    BeautifulSoap
        25
    BeautifulSoap  
       2023-01-28 16:19:24 +08:00   1
    Go 的泛型类型都必须经过实例化。泛型实例化之后的类型就成为了和 int, string, float32 这些类型一样确定的类型

    你用 map[string]string 实例化泛型类型 Response[T] 的话,那么实例化之后的类型就是 Response[map[string]string]。用 any 实例化的话实例化得到的则是 Response[any]。编译器的类型检查是不会去关心你这泛型类型到底是怎么个结构的,所以你把一个 Response[map[string]string] 类型的变量赋值给另一个 Response[any] 类型的变量那肯定要报错(在编译器的眼里,这相当于你把 int 类型变量赋值给 string 变量一样)

    有兴趣可以看看我之前写的关于泛型的教程,go 目前的泛型并不是完整功能的泛型,所以并不能做到其他教程里那样理所当然的事情
    t/844084
    BeautifulSoap
        26
    BeautifulSoap  
       2023-01-28 16:20:35 +08:00
    @BeautifulSoap 打错字“go 目前的泛型并不是完整功能的泛型,所以并不能做到其他语言里那样理所当然的事情”
    jorneyr
        27
    jorneyr  
    OP
       2023-01-28 16:37:16 +08:00
    @BeautifulSoap
    嗯,那我这个想统一处理的方式,还想接口好看,在 go 里估计就行不通了。
    lazyfighter
        28
    lazyfighter  
       2023-01-29 14:50:53 +08:00
    我在想 java 是因为任意的对象都继承自 object ,所以泛型 T 不指定就变成了 Object 引用,即父类指向子类引用, 所以感觉 go 里面应该用 interface ,纯属猜测
    jorneyr
        29
    jorneyr  
    OP
       2023-01-29 15:34:21 +08:00
    @lazyfighter 用 interface 的话,我这个场景的统一返回对象直接不使用范型就可以了。

    type Response struct {
    Code int `json:"code"` // 业务逻辑相关的 code ,不是 HTTP Status Code
    Success bool `json:"success"` // 业务逻辑处理成功时为 true ,错误时为 false
    Msg string `json:"msg"` // 请求的描述
    Data any `json:"data,omitempty"` // 请求的 payload
    }
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3715 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 00:51 PVG 08:51 LAX 17:51 JFK 20:51
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86